summaryrefslogtreecommitdiffstats
path: root/tests/topotests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--tests/topotests/.gitignore4
-rw-r--r--tests/topotests/Dockerfile87
-rw-r--r--tests/topotests/README.md1
-rw-r--r--tests/topotests/all_protocol_startup/r1/babeld.conf4
-rw-r--r--tests/topotests/all_protocol_startup/r1/bgpd.conf60
-rw-r--r--tests/topotests/all_protocol_startup/r1/ip_nht.ref72
-rw-r--r--tests/topotests/all_protocol_startup/r1/ipv4_routes.ref32
-rw-r--r--tests/topotests/all_protocol_startup/r1/ipv6_nht.ref13
-rw-r--r--tests/topotests/all_protocol_startup/r1/ipv6_routes.ref29
-rw-r--r--tests/topotests/all_protocol_startup/r1/isisd.conf21
-rw-r--r--tests/topotests/all_protocol_startup/r1/ldpd.conf25
-rw-r--r--tests/topotests/all_protocol_startup/r1/nhrpd.conf1
-rw-r--r--tests/topotests/all_protocol_startup/r1/ospf6d.conf19
-rw-r--r--tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v416
-rw-r--r--tests/topotests/all_protocol_startup/r1/ospfd.conf16
-rw-r--r--tests/topotests/all_protocol_startup/r1/pbrd.conf10
-rw-r--r--tests/topotests/all_protocol_startup/r1/rip_status.ref15
-rw-r--r--tests/topotests/all_protocol_startup/r1/ripd.conf15
-rw-r--r--tests/topotests/all_protocol_startup/r1/ripng_status.ref14
-rw-r--r--tests/topotests/all_protocol_startup/r1/ripngd.conf14
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref9
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref9
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref10
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref7
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref9
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref7
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref10
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref8
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref10
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref24
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface0
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref46
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref28
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref3
-rw-r--r--tests/topotests/all_protocol_startup/r1/show_route_map.ref72
-rw-r--r--tests/topotests/all_protocol_startup/r1/zebra.conf120
-rw-r--r--tests/topotests/all_protocol_startup/test_all_protocol_startup.dot61
-rw-r--r--tests/topotests/all_protocol_startup/test_all_protocol_startup.pdfbin0 -> 21760 bytes
-rw-r--r--tests/topotests/all_protocol_startup/test_all_protocol_startup.py1684
-rwxr-xr-xtests/topotests/analyze.py271
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/__init__.py0
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf5
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json98
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf23
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json80
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json17
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json15
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf8
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf9
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf5
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json52
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf29
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json80
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json17
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json15
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf7
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot58
-rw-r--r--tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py239
-rw-r--r--tests/topotests/bfd_isis_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/bfdd.conf19
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/isisd.conf36
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref74
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref70
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref16
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref16
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref9
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref9
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref74
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref74
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref74
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref70
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref70
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref70
-rw-r--r--tests/topotests/bfd_isis_topo1/rt1/zebra.conf25
-rw-r--r--tests/topotests/bfd_isis_topo1/rt2/bfdd.conf13
-rw-r--r--tests/topotests/bfd_isis_topo1/rt2/isisd.conf31
-rw-r--r--tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref9
-rw-r--r--tests/topotests/bfd_isis_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/bfd_isis_topo1/rt3/bfdd.conf13
-rw-r--r--tests/topotests/bfd_isis_topo1/rt3/isisd.conf33
-rw-r--r--tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref9
-rw-r--r--tests/topotests/bfd_isis_topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/bfd_isis_topo1/rt4/bfdd.conf5
-rw-r--r--tests/topotests/bfd_isis_topo1/rt4/isisd.conf30
-rw-r--r--tests/topotests/bfd_isis_topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/bfd_isis_topo1/rt5/bfdd.conf5
-rw-r--r--tests/topotests/bfd_isis_topo1/rt5/isisd.conf30
-rw-r--r--tests/topotests/bfd_isis_topo1/rt5/zebra.conf22
-rw-r--r--tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py268
-rw-r--r--tests/topotests/bfd_ospf_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf9
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf25
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf32
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref74
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref70
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref26
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref28
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref15
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref15
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref74
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref74
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref74
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref70
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref70
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref70
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt1/zebra.conf25
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf7
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf23
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf30
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref14
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf7
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf23
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf30
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref14
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf5
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf22
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf29
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf5
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf22
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf29
-rw-r--r--tests/topotests/bfd_ospf_topo1/rt5/zebra.conf22
-rwxr-xr-xtests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py273
-rw-r--r--tests/topotests/bfd_profiles_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json38
-rw-r--r--tests/topotests/bfd_profiles_topo1/r1/bfdd.conf15
-rw-r--r--tests/topotests/bfd_profiles_topo1/r1/ospfd.conf11
-rw-r--r--tests/topotests/bfd_profiles_topo1/r1/zebra.conf9
-rw-r--r--tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json40
-rw-r--r--tests/topotests/bfd_profiles_topo1/r2/bfdd.conf19
-rw-r--r--tests/topotests/bfd_profiles_topo1/r2/bgpd.conf21
-rw-r--r--tests/topotests/bfd_profiles_topo1/r2/zebra.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json40
-rw-r--r--tests/topotests/bfd_profiles_topo1/r3/bfdd.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r3/bgpd.conf14
-rw-r--r--tests/topotests/bfd_profiles_topo1/r3/isisd.conf17
-rw-r--r--tests/topotests/bfd_profiles_topo1/r3/zebra.conf12
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json41
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/bfdd.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/bgpd.conf18
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/isisd.conf17
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r4/zebra.conf12
-rw-r--r--tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json21
-rw-r--r--tests/topotests/bfd_profiles_topo1/r5/bfdd.conf11
-rw-r--r--tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r5/zebra.conf6
-rw-r--r--tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json20
-rw-r--r--tests/topotests/bfd_profiles_topo1/r6/bfdd.conf11
-rw-r--r--tests/topotests/bfd_profiles_topo1/r6/ospfd.conf10
-rw-r--r--tests/topotests/bfd_profiles_topo1/r6/zebra.conf6
-rw-r--r--tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot97
-rw-r--r--tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.pngbin0 -> 43508 bytes
-rw-r--r--tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py173
-rw-r--r--tests/topotests/bfd_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_topo1/r1/bfdd.conf11
-rw-r--r--tests/topotests/bfd_topo1/r1/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_topo1/r1/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_topo1/r1/bgpd.conf10
-rw-r--r--tests/topotests/bfd_topo1/r1/peers.json8
-rw-r--r--tests/topotests/bfd_topo1/r1/zebra.conf3
-rw-r--r--tests/topotests/bfd_topo1/r2/bfdd.conf17
-rw-r--r--tests/topotests/bfd_topo1/r2/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_topo1/r2/bgp_summary.json19
-rw-r--r--tests/topotests/bfd_topo1/r2/bgpd.conf16
-rw-r--r--tests/topotests/bfd_topo1/r2/peers.json17
-rw-r--r--tests/topotests/bfd_topo1/r2/zebra.conf9
-rw-r--r--tests/topotests/bfd_topo1/r3/bfdd.conf12
-rw-r--r--tests/topotests/bfd_topo1/r3/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_topo1/r3/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_topo1/r3/bgpd.conf10
-rw-r--r--tests/topotests/bfd_topo1/r3/peers.json6
-rw-r--r--tests/topotests/bfd_topo1/r3/zebra.conf3
-rw-r--r--tests/topotests/bfd_topo1/r4/bfdd.conf12
-rw-r--r--tests/topotests/bfd_topo1/r4/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_topo1/r4/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_topo1/r4/bgpd.conf10
-rw-r--r--tests/topotests/bfd_topo1/r4/peers.json6
-rw-r--r--tests/topotests/bfd_topo1/r4/zebra.conf3
-rw-r--r--tests/topotests/bfd_topo1/test_bfd_topo1.dot73
-rw-r--r--tests/topotests/bfd_topo1/test_bfd_topo1.jpgbin0 -> 25713 bytes
-rw-r--r--tests/topotests/bfd_topo1/test_bfd_topo1.py226
-rw-r--r--tests/topotests/bfd_topo2/__init__.py0
-rw-r--r--tests/topotests/bfd_topo2/r1/bfdd.conf10
-rw-r--r--tests/topotests/bfd_topo2/r1/bgpd.conf15
-rw-r--r--tests/topotests/bfd_topo2/r1/ipv4_routes.json59
-rw-r--r--tests/topotests/bfd_topo2/r1/ipv6_routes.json53
-rw-r--r--tests/topotests/bfd_topo2/r1/peers.json31
-rw-r--r--tests/topotests/bfd_topo2/r1/zebra.conf6
-rw-r--r--tests/topotests/bfd_topo2/r2/bfdd.conf5
-rw-r--r--tests/topotests/bfd_topo2/r2/bgpd.conf18
-rw-r--r--tests/topotests/bfd_topo2/r2/ipv4_routes.json92
-rw-r--r--tests/topotests/bfd_topo2/r2/ipv6_routes.json53
-rw-r--r--tests/topotests/bfd_topo2/r2/ospf6d.conf11
-rw-r--r--tests/topotests/bfd_topo2/r2/ospfd.conf11
-rw-r--r--tests/topotests/bfd_topo2/r2/peers.json45
-rw-r--r--tests/topotests/bfd_topo2/r2/zebra.conf15
-rw-r--r--tests/topotests/bfd_topo2/r3/bfdd.conf5
-rw-r--r--tests/topotests/bfd_topo2/r3/ipv4_routes.json93
-rw-r--r--tests/topotests/bfd_topo2/r3/ipv6_routes.json2
-rw-r--r--tests/topotests/bfd_topo2/r3/ospfd.conf10
-rw-r--r--tests/topotests/bfd_topo2/r3/peers.json17
-rw-r--r--tests/topotests/bfd_topo2/r3/zebra.conf6
-rw-r--r--tests/topotests/bfd_topo2/r4/bfdd.conf10
-rw-r--r--tests/topotests/bfd_topo2/r4/ipv4_routes.json21
-rw-r--r--tests/topotests/bfd_topo2/r4/ipv6_routes.json53
-rw-r--r--tests/topotests/bfd_topo2/r4/ospf6d.conf10
-rw-r--r--tests/topotests/bfd_topo2/r4/peers.json31
-rw-r--r--tests/topotests/bfd_topo2/r4/zebra.conf6
-rw-r--r--tests/topotests/bfd_topo2/test_bfd_topo2.dot73
-rw-r--r--tests/topotests/bfd_topo2/test_bfd_topo2.jpgbin0 -> 24206 bytes
-rw-r--r--tests/topotests/bfd_topo2/test_bfd_topo2.py163
-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.conf23
-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.conf17
-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.conf22
-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.conf18
-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.py171
-rw-r--r--tests/topotests/bfd_vrf_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/bfdd.conf15
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/bgpd.conf11
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/peers.json8
-rw-r--r--tests/topotests/bfd_vrf_topo1/r1/zebra.conf3
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/bfdd.conf23
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json19
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/bgpd.conf16
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/peers.json17
-rw-r--r--tests/topotests/bfd_vrf_topo1/r2/zebra.conf9
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/bfdd.conf14
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/bgpd.conf10
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/peers.json6
-rw-r--r--tests/topotests/bfd_vrf_topo1/r3/zebra.conf3
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/bfdd.conf12
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json52
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json11
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/bgpd.conf10
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/peers.json6
-rw-r--r--tests/topotests/bfd_vrf_topo1/r4/zebra.conf3
-rw-r--r--tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot73
-rw-r--r--tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpgbin0 -> 25713 bytes
-rw-r--r--tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py280
-rw-r--r--tests/topotests/bfd_vrflite_topo1/__init__.py0
-rw-r--r--tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json96
-rw-r--r--tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf26
-rw-r--r--tests/topotests/bfd_vrflite_topo1/r1/zebra.conf24
-rw-r--r--tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf26
-rw-r--r--tests/topotests/bfd_vrflite_topo1/r2/zebra.conf24
-rw-r--r--tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py153
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/__init__.py0
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf21
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf11
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf11
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py148
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/__init__.py0
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py119
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/__init__.py0
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py122
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/exabgp.env53
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf30
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf13
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf10
-rw-r--r--tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py292
-rw-r--r--tests/topotests/bgp_aggregator_zero/__init__.py0
-rw-r--r--tests/topotests/bgp_aggregator_zero/exabgp.env53
-rw-r--r--tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg18
-rw-r--r--tests/topotests/bgp_aggregator_zero/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aggregator_zero/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py133
-rw-r--r--tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json266
-rw-r--r--tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py982
-rw-r--r--tests/topotests/bgp_as_override/__init__.py0
-rw-r--r--tests/topotests/bgp_as_override/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_as_override/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_as_override/r2/bgpd.conf10
-rw-r--r--tests/topotests/bgp_as_override/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_as_override/r3/bgpd.conf13
-rw-r--r--tests/topotests/bgp_as_override/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_as_override/r4/bgpd.conf7
-rw-r--r--tests/topotests/bgp_as_override/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_as_override/test_bgp_as_override.py122
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/__init__.py0
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf7
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf7
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py115
-rw-r--r--tests/topotests/bgp_aspath_zero/__init__.py0
-rw-r--r--tests/topotests/bgp_aspath_zero/exabgp.env53
-rw-r--r--tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg17
-rw-r--r--tests/topotests/bgp_aspath_zero/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_aspath_zero/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py113
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd.conf18
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf39
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf37
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd_vrf.conf20
-rw-r--r--tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R1/empty.conf0
-rw-r--r--tests/topotests/bgp_auth/R1/ospfd.conf22
-rw-r--r--tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf26
-rw-r--r--tests/topotests/bgp_auth/R1/ospfd_vrf.conf22
-rw-r--r--tests/topotests/bgp_auth/R1/zebra.conf20
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf37
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf37
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd_vrf.conf18
-rw-r--r--tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R2/empty.conf0
-rw-r--r--tests/topotests/bgp_auth/R2/ospfd.conf22
-rw-r--r--tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf26
-rw-r--r--tests/topotests/bgp_auth/R2/ospfd_vrf.conf22
-rw-r--r--tests/topotests/bgp_auth/R2/zebra.conf20
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd.conf18
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf37
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf37
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd_vrf.conf18
-rw-r--r--tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf18
-rw-r--r--tests/topotests/bgp_auth/R3/empty.conf0
-rw-r--r--tests/topotests/bgp_auth/R3/ospfd.conf22
-rw-r--r--tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf26
-rw-r--r--tests/topotests/bgp_auth/R3/ospfd_vrf.conf22
-rw-r--r--tests/topotests/bgp_auth/R3/zebra.conf20
-rw-r--r--tests/topotests/bgp_auth/bgp_auth_common.py279
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth1.py245
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth2.py253
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth3.py234
-rw-r--r--tests/topotests/bgp_auth/test_bgp_auth4.py251
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json115
-rw-r--r--tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py1182
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/__init__.py0
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf6
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf6
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py124
-rw-r--r--tests/topotests/bgp_blackhole_community/__init__.py0
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_blackhole_community/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_blackhole_community/r2/zebra.conf12
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r4/bgpd.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py153
-rw-r--r--tests/topotests/bgp_comm_list_delete/__init__.py0
-rw-r--r--tests/topotests/bgp_comm_list_delete/r1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_comm_list_delete/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_comm_list_delete/r2/bgpd.conf13
-rw-r--r--tests/topotests/bgp_comm_list_delete/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py112
-rw-r--r--tests/topotests/bgp_communities_topo1/bgp_communities.json175
-rw-r--r--tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json191
-rw-r--r--tests/topotests/bgp_communities_topo1/test_bgp_communities.py643
-rw-r--r--tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py404
-rw-r--r--tests/topotests/bgp_community_alias/__init__.py0
-rw-r--r--tests/topotests/bgp_community_alias/r1/bgpd.conf26
-rw-r--r--tests/topotests/bgp_community_alias/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_community_alias/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_community_alias/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_community_alias/test_bgp-community-alias.py149
-rw-r--r--tests/topotests/bgp_community_change_update/__init__.py0
-rw-r--r--tests/topotests/bgp_community_change_update/c1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_community_change_update/c1/zebra.conf6
-rw-r--r--tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py222
-rw-r--r--tests/topotests/bgp_community_change_update/x1/bgpd.conf20
-rw-r--r--tests/topotests/bgp_community_change_update/x1/zebra.conf9
-rw-r--r--tests/topotests/bgp_community_change_update/y1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_community_change_update/y1/zebra.conf12
-rw-r--r--tests/topotests/bgp_community_change_update/y2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_community_change_update/y2/zebra.conf12
-rw-r--r--tests/topotests/bgp_community_change_update/y3/bgpd.conf18
-rw-r--r--tests/topotests/bgp_community_change_update/y3/zebra.conf12
-rw-r--r--tests/topotests/bgp_community_change_update/z1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_community_change_update/z1/zebra.conf12
-rw-r--r--tests/topotests/bgp_conditional_advertisement/__init__.py0
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf31
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r1/zebra.conf19
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf46
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r2/zebra.conf15
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_conditional_advertisement/r3/zebra.conf12
-rw-r--r--tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py1310
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py0
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf28
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf3
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf10
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py154
-rw-r--r--tests/topotests/bgp_confederation_astype/__init__.py0
-rw-r--r--tests/topotests/bgp_confederation_astype/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_confederation_astype/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_confederation_astype/r2/bgpd.conf13
-rw-r--r--tests/topotests/bgp_confederation_astype/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_confederation_astype/r3/bgpd.conf10
-rw-r--r--tests/topotests/bgp_confederation_astype/r3/zebra.conf4
-rw-r--r--tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py140
-rw-r--r--tests/topotests/bgp_default_afi_safi/__init__.py0
-rw-r--r--tests/topotests/bgp_default_afi_safi/r1/bgpd.conf3
-rw-r--r--tests/topotests/bgp_default_afi_safi/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_default_afi_safi/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_default_afi_safi/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_afi_safi/r3/bgpd.conf5
-rw-r--r--tests/topotests/bgp_default_afi_safi/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_afi_safi/r4/bgpd.conf5
-rw-r--r--tests/topotests/bgp_default_afi_safi/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py158
-rw-r--r--tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json325
-rw-r--r--tests/topotests/bgp_default_originate/bgp_default_originate_2links.json136
-rw-r--r--tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json294
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py1810
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py2539
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py2439
-rw-r--r--tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py2540
-rw-r--r--tests/topotests/bgp_default_originate/test_default_orginate_vrf.py1389
-rw-r--r--tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py2104
-rw-r--r--tests/topotests/bgp_default_route/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_default_route/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route/test_bgp_default-originate.py115
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py110
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf13
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py134
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf19
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py120
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/__init__.py0
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py112
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/__init__.py0
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf13
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf9
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf7
-rw-r--r--tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py142
-rw-r--r--tests/topotests/bgp_distance_change/__init__.py0
-rwxr-xr-xtests/topotests/bgp_distance_change/bgp_admin_dist.json402
-rwxr-xr-xtests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json429
-rw-r--r--tests/topotests/bgp_distance_change/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_distance_change/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_distance_change/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_distance_change/r2/zebra.conf9
-rwxr-xr-xtests/topotests/bgp_distance_change/test_bgp_admin_dist.py1282
-rwxr-xr-xtests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py900
-rw-r--r--tests/topotests/bgp_distance_change/test_bgp_distance_change.py133
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/__init__.py0
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py96
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py0
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf7
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf3
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf5
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf4
-rw-r--r--tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py125
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/__init__.py0
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf5
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf7
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf9
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf4
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf6
-rw-r--r--tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py171
-rw-r--r--tests/topotests/bgp_ecmp_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot206
-rw-r--r--tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdfbin0 -> 21367 bytes
-rw-r--r--tests/topotests/bgp_ecmp_topo1/exabgp.env54
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer1/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer10/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer11/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer12/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer13/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer14/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer15/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer16/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer17/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer18/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer19/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer2/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer20/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer3/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer4/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer5/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer6/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer7/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer8/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_ecmp_topo1/peer9/exa-send.py66
-rw-r--r--tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf51
-rw-r--r--tests/topotests/bgp_ecmp_topo1/r1/summary.txt131
-rw-r--r--tests/topotests/bgp_ecmp_topo1/r1/summary20.txt129
-rw-r--r--tests/topotests/bgp_ecmp_topo1/r1/zebra.conf16
-rw-r--r--tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py183
-rwxr-xr-xtests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json834
-rwxr-xr-xtests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json844
-rw-r--r--tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py753
-rw-r--r--tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py754
-rw-r--r--tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json232
-rw-r--r--tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py270
-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
-rw-r--r--tests/topotests/bgp_evpn_mh/test_evpn_mh.py809
-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.conf27
-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.conf28
-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.conf29
-rw-r--r--tests/topotests/bgp_evpn_mh/torm22/evpn.conf20
-rw-r--r--tests/topotests/bgp_evpn_mh/torm22/pim.conf13
-rw-r--r--tests/topotests/bgp_evpn_mh/torm22/zebra.conf29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json192
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json8
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json192
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json6
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json6
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf30
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf14
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json56
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json56
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json55
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json55
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json192
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json68
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json192
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json27
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json6
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json28
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json28
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json6
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf14
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf14
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json56
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json56
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json29
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py0
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf18
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf4
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf4
-rwxr-xr-xtests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py414
-rw-r--r--tests/topotests/bgp_evpn_rt5/__init__.py0
-rw-r--r--tests/topotests/bgp_evpn_rt5/r1/bgpd.conf26
-rw-r--r--tests/topotests/bgp_evpn_rt5/r1/zebra.conf22
-rw-r--r--tests/topotests/bgp_evpn_rt5/r2/bgpd.conf27
-rw-r--r--tests/topotests/bgp_evpn_rt5/r2/zebra.conf18
-rw-r--r--tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py236
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf13
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf7
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json13
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf8
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json12
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf9
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf6
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf3
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf1
-rw-r--r--tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf3
-rwxr-xr-xtests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py440
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/__init__.py0
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py106
-rw-r--r--tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json6
-rw-r--r--tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json11
-rw-r--r--tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json11
-rw-r--r--tests/topotests/bgp_features/r1/bgp_shutdown_summary.json19
-rw-r--r--tests/topotests/bgp_features/r1/bgp_summary.json27
-rw-r--r--tests/topotests/bgp_features/r1/bgpd.conf41
-rw-r--r--tests/topotests/bgp_features/r1/ip_route.json364
-rw-r--r--tests/topotests/bgp_features/r1/ip_route_norib.json281
-rw-r--r--tests/topotests/bgp_features/r1/ospf6d.conf21
-rw-r--r--tests/topotests/bgp_features/r1/ospf_neighbor.json16
-rw-r--r--tests/topotests/bgp_features/r1/ospfd.conf26
-rw-r--r--tests/topotests/bgp_features/r1/show_bgp.json350
-rw-r--r--tests/topotests/bgp_features/r1/show_bgp_metric_test.json57
-rw-r--r--tests/topotests/bgp_features/r1/zebra.conf28
-rw-r--r--tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json6
-rw-r--r--tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json11
-rw-r--r--tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json11
-rw-r--r--tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json11
-rw-r--r--tests/topotests/bgp_features/r2/bgp_shutdown_summary.json19
-rw-r--r--tests/topotests/bgp_features/r2/bgp_summary.json27
-rw-r--r--tests/topotests/bgp_features/r2/bgpd.conf41
-rw-r--r--tests/topotests/bgp_features/r2/ospf6d.conf21
-rw-r--r--tests/topotests/bgp_features/r2/ospf_neighbor.json16
-rw-r--r--tests/topotests/bgp_features/r2/ospfd.conf26
-rw-r--r--tests/topotests/bgp_features/r2/show_bgp.json349
-rw-r--r--tests/topotests/bgp_features/r2/show_bgp_metric_test.json57
-rw-r--r--tests/topotests/bgp_features/r2/zebra.conf28
-rw-r--r--tests/topotests/bgp_features/r3/bgp_summary.json0
-rw-r--r--tests/topotests/bgp_features/r3/ospf6d.conf21
-rw-r--r--tests/topotests/bgp_features/r3/ospf_neighbor.json16
-rw-r--r--tests/topotests/bgp_features/r3/ospfd.conf26
-rw-r--r--tests/topotests/bgp_features/r3/zebra.conf23
-rw-r--r--tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json11
-rw-r--r--tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json11
-rw-r--r--tests/topotests/bgp_features/r4/bgp_shutdown_summary.json14
-rw-r--r--tests/topotests/bgp_features/r4/bgp_summary.json18
-rw-r--r--tests/topotests/bgp_features/r4/bgpd.conf34
-rw-r--r--tests/topotests/bgp_features/r4/show_bgp_metric_test.json27
-rw-r--r--tests/topotests/bgp_features/r4/zebra.conf18
-rw-r--r--tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json6
-rw-r--r--tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json11
-rw-r--r--tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json11
-rw-r--r--tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json11
-rw-r--r--tests/topotests/bgp_features/r5/bgp_summary.json19
-rw-r--r--tests/topotests/bgp_features/r5/bgpd.conf34
-rw-r--r--tests/topotests/bgp_features/r5/show_bgp_metric_test.json27
-rw-r--r--tests/topotests/bgp_features/r5/zebra.conf18
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.dot83
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.pdfbin0 -> 20710 bytes
-rw-r--r--tests/topotests/bgp_features/test_bgp_features.py1102
-rw-r--r--tests/topotests/bgp_flowspec/__init__.py0
-rw-r--r--tests/topotests/bgp_flowspec/exabgp.env54
-rw-r--r--tests/topotests/bgp_flowspec/peer1/exabgp.cfg32
-rw-r--r--tests/topotests/bgp_flowspec/r1/bgpd.conf19
-rw-r--r--tests/topotests/bgp_flowspec/r1/summary.txt50
-rw-r--r--tests/topotests/bgp_flowspec/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py203
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json115
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py1542
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py404
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py2753
-rw-r--r--tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py1686
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json334
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py1515
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py1216
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py1367
-rw-r--r--tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py1024
-rw-r--r--tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json222
-rw-r--r--tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py554
-rw-r--r--tests/topotests/bgp_gr_notification/__init__.py0
-rw-r--r--tests/topotests/bgp_gr_notification/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_gr_notification/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_gr_notification/r2/bgpd.conf11
-rw-r--r--tests/topotests/bgp_gr_notification/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py224
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/__init__.py0
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py131
-rw-r--r--tests/topotests/bgp_gshut/__init__.py0
-rw-r--r--tests/topotests/bgp_gshut/r1/bgp_route_1.json12
-rw-r--r--tests/topotests/bgp_gshut/r1/bgp_route_2.json17
-rw-r--r--tests/topotests/bgp_gshut/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_gshut/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_gshut/r2/bgp_sum_1.json15
-rw-r--r--tests/topotests/bgp_gshut/r2/bgp_sum_2.json15
-rw-r--r--tests/topotests/bgp_gshut/r2/bgpd.conf20
-rw-r--r--tests/topotests/bgp_gshut/r2/zebra.conf13
-rw-r--r--tests/topotests/bgp_gshut/r3/bgp_route_1.json9
-rw-r--r--tests/topotests/bgp_gshut/r3/bgp_route_2.json16
-rw-r--r--tests/topotests/bgp_gshut/r3/bgpd.conf11
-rw-r--r--tests/topotests/bgp_gshut/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_gshut/r4/bgpd.conf11
-rw-r--r--tests/topotests/bgp_gshut/r4/zebra.conf9
-rw-r--r--tests/topotests/bgp_gshut/r5/bgp_route_1.json9
-rw-r--r--tests/topotests/bgp_gshut/r5/bgp_route_2.json16
-rw-r--r--tests/topotests/bgp_gshut/r5/bgpd.conf7
-rw-r--r--tests/topotests/bgp_gshut/r5/zebra.conf9
-rw-r--r--tests/topotests/bgp_gshut/test_bgp_gshut.py356
-rw-r--r--tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json221
-rw-r--r--tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json221
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py606
-rw-r--r--tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py653
-rw-r--r--tests/topotests/bgp_instance_del_test/__init__.py0
l---------tests/topotests/bgp_instance_del_test/ce11
l---------tests/topotests/bgp_instance_del_test/ce21
l---------tests/topotests/bgp_instance_del_test/ce31
l---------tests/topotests/bgp_instance_del_test/ce41
l---------tests/topotests/bgp_instance_del_test/customize.py1
l---------tests/topotests/bgp_instance_del_test/r11
l---------tests/topotests/bgp_instance_del_test/r21
l---------tests/topotests/bgp_instance_del_test/r31
l---------tests/topotests/bgp_instance_del_test/r41
l---------tests/topotests/bgp_instance_del_test/scripts1
-rwxr-xr-xtests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py99
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/__init__.py0
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf11
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf8
-rw-r--r--tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py127
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json85
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json95
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json97
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json95
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json97
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py963
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py630
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py779
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py988
-rw-r--r--tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py324
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/__init__.py0
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json40
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json34
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json40
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json21
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot44
-rw-r--r--tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py148
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf32
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf32
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf32
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py146
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf42
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf23
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf12
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf27
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf33
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf25
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf19
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf31
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf41
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf23
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf32
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf41
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf23
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf12
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf26
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py193
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py71
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py55
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py114
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py77
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf55
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf55
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf0
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf45
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf45
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py229
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf55
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf24
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf12
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf25
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf35
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf26
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf19
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf28
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf48
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf24
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf17
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf29
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf72
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf24
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf12
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf28
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py59
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py64
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py83
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py74
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py888
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py120
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py30
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py22
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py87
-rw-r--r--tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py234
-rwxr-xr-xtests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py128
-rw-r--r--tests/topotests/bgp_large_community/__init__.py0
-rw-r--r--tests/topotests/bgp_large_community/bgp_large_community_topo_1.json262
-rw-r--r--tests/topotests/bgp_large_community/bgp_large_community_topo_2.json344
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py1229
-rw-r--r--tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py2233
-rwxr-xr-xtests/topotests/bgp_link_bw_ip/__init__.py0
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json19
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json32
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json32
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json32
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json29
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json15
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json15
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/v4_route.json84
-rw-r--r--tests/topotests/bgp_link_bw_ip/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_link_bw_ip/r10/bgpd.conf17
-rw-r--r--tests/topotests/bgp_link_bw_ip/r10/zebra.conf6
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json19
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json19
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json32
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/bgpd.conf13
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json19
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json20
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json15
-rw-r--r--tests/topotests/bgp_link_bw_ip/r2/zebra.conf10
-rw-r--r--tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json29
-rw-r--r--tests/topotests/bgp_link_bw_ip/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_link_bw_ip/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json23
-rw-r--r--tests/topotests/bgp_link_bw_ip/r4/bgpd.conf32
-rw-r--r--tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json21
-rw-r--r--tests/topotests/bgp_link_bw_ip/r4/zebra.conf10
-rw-r--r--tests/topotests/bgp_link_bw_ip/r5/bgpd.conf23
-rw-r--r--tests/topotests/bgp_link_bw_ip/r5/zebra.conf7
-rw-r--r--tests/topotests/bgp_link_bw_ip/r6/bgpd.conf24
-rw-r--r--tests/topotests/bgp_link_bw_ip/r6/zebra.conf7
-rw-r--r--tests/topotests/bgp_link_bw_ip/r7/bgpd.conf17
-rw-r--r--tests/topotests/bgp_link_bw_ip/r7/zebra.conf6
-rw-r--r--tests/topotests/bgp_link_bw_ip/r8/bgpd.conf17
-rw-r--r--tests/topotests/bgp_link_bw_ip/r8/zebra.conf6
-rw-r--r--tests/topotests/bgp_link_bw_ip/r9/bgpd.conf17
-rw-r--r--tests/topotests/bgp_link_bw_ip/r9/zebra.conf6
-rw-r--r--tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py588
-rw-r--r--tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json154
-rwxr-xr-xtests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py149
-rw-r--r--tests/topotests/bgp_llgr/__init__.py0
-rw-r--r--tests/topotests/bgp_llgr/r0/bgpd.conf12
-rw-r--r--tests/topotests/bgp_llgr/r0/zebra.conf7
-rw-r--r--tests/topotests/bgp_llgr/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_llgr/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_llgr/r2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_llgr/r2/zebra.conf13
-rw-r--r--tests/topotests/bgp_llgr/r3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_llgr/r3/zebra.conf4
-rw-r--r--tests/topotests/bgp_llgr/r4/bgpd.conf13
-rw-r--r--tests/topotests/bgp_llgr/r4/zebra.conf7
-rw-r--r--tests/topotests/bgp_llgr/test_bgp_llgr.py198
-rw-r--r--tests/topotests/bgp_local_as_private_remove/__init__.py0
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf5
-rw-r--r--tests/topotests/bgp_local_as_private_remove/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py114
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/bgpd.conf21
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R1/zebra.conf6
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/bgpd.conf23
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo1/R2/zebra.conf11
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/bgpd.conf523
-rw-r--r--tests/topotests/bgp_lu_topo1/R3/zebra.conf9
-rw-r--r--tests/topotests/bgp_lu_topo1/test_bgp_lu.py181
-rw-r--r--tests/topotests/bgp_lu_topo2/R1/bgpd.conf29
-rw-r--r--tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo2/R1/zebra.conf13
-rw-r--r--tests/topotests/bgp_lu_topo2/R2/bgpd.conf27
-rw-r--r--tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json8
-rw-r--r--tests/topotests/bgp_lu_topo2/R2/zebra.conf14
-rw-r--r--tests/topotests/bgp_lu_topo2/R3/bgpd.conf70
-rw-r--r--tests/topotests/bgp_lu_topo2/R3/staticd.conf5
-rw-r--r--tests/topotests/bgp_lu_topo2/R3/zebra.conf11
-rw-r--r--tests/topotests/bgp_lu_topo2/R4/bgpd.conf23
-rw-r--r--tests/topotests/bgp_lu_topo2/R4/zebra.conf9
-rw-r--r--tests/topotests/bgp_lu_topo2/test_bgp_lu2.py221
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py0
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py113
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/__init__.py0
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf13
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py200
-rw-r--r--tests/topotests/bgp_minimum_holdtime/__init__.py0
-rw-r--r--tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_minimum_holdtime/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_minimum_holdtime/r2/zebra.conf6
-rwxr-xr-xtests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py98
-rw-r--r--tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json884
-rw-r--r--tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py6290
-rw-r--r--tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json1553
-rw-r--r--tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py3852
-rw-r--r--tests/topotests/bgp_multiview_topo1/README.md125
-rw-r--r--tests/topotests/bgp_multiview_topo1/exabgp.env54
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer1/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer2/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer3/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer4/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer5/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer6/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer7/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg21
-rwxr-xr-xtests/topotests/bgp_multiview_topo1/peer8/exa-send.py31
-rw-r--r--tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_multiview_topo1/r1/bgpd.conf61
-rw-r--r--tests/topotests/bgp_multiview_topo1/r1/view_1.json728
-rw-r--r--tests/topotests/bgp_multiview_topo1/r1/view_2.json489
-rw-r--r--tests/topotests/bgp_multiview_topo1/r1/view_3.json728
-rw-r--r--tests/topotests/bgp_multiview_topo1/r1/zebra.conf23
-rw-r--r--tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py238
-rw-r--r--tests/topotests/bgp_orf/__init__.py0
-rw-r--r--tests/topotests/bgp_orf/r1/bgpd.conf9
-rw-r--r--tests/topotests/bgp_orf/r1/zebra.conf8
-rw-r--r--tests/topotests/bgp_orf/r2/bgpd.conf9
-rw-r--r--tests/topotests/bgp_orf/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_orf/test_bgp_orf.py159
-rw-r--r--tests/topotests/bgp_path_attributes_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json363
-rw-r--r--tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py1541
-rw-r--r--tests/topotests/bgp_peer_group/__init__.py0
-rw-r--r--tests/topotests/bgp_peer_group/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_peer_group/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer_group/r2/bgpd.conf7
-rw-r--r--tests/topotests/bgp_peer_group/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer_group/r3/bgpd.conf7
-rw-r--r--tests/topotests/bgp_peer_group/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_peer_group/test_bgp_peer-group.py97
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/exabgp.env53
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py19
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf16
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json50
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json50
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json33
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json33
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json35
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json36
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json33
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json23
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json21
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json23
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf27
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf19
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf4
-rw-r--r--tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf19
-rwxr-xr-xtests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py385
-rw-r--r--tests/topotests/bgp_prefix_list_any/__init__.py0
-rw-r--r--tests/topotests/bgp_prefix_list_any/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_prefix_list_any/r1/zebra.conf5
-rw-r--r--tests/topotests/bgp_prefix_list_any/r2/bgpd.conf50
-rw-r--r--tests/topotests/bgp_prefix_list_any/r2/zebra.conf5
-rw-r--r--tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py105
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/__init__.py0
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/prefix_lists.json123
-rw-r--r--tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py1336
-rw-r--r--tests/topotests/bgp_prefix_sid/__init__.py0
-rw-r--r--tests/topotests/bgp_prefix_sid/exabgp.env53
-rw-r--r--tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg103
-rw-r--r--tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg19
-rw-r--r--tests/topotests/bgp_prefix_sid/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_prefix_sid/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py185
-rw-r--r--tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg29
-rw-r--r--tests/topotests/bgp_prefix_sid2/peer1/exabgp.env53
-rw-r--r--tests/topotests/bgp_prefix_sid2/r1/bgpd.conf26
-rw-r--r--tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json50
-rw-r--r--tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json50
-rw-r--r--tests/topotests/bgp_prefix_sid2/r1/zebra.conf7
-rwxr-xr-xtests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py116
-rw-r--r--tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json321
-rw-r--r--tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py2420
-rw-r--r--tests/topotests/bgp_reject_as_sets/__init__.py0
-rw-r--r--tests/topotests/bgp_reject_as_sets/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_reject_as_sets/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_reject_as_sets/r2/bgpd.conf13
-rw-r--r--tests/topotests/bgp_reject_as_sets/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_reject_as_sets/r3/bgpd.conf11
-rw-r--r--tests/topotests/bgp_reject_as_sets/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py151
-rw-r--r--tests/topotests/bgp_remove_private_as/r1/bgpd.conf53
-rw-r--r--tests/topotests/bgp_remove_private_as/r1/zebra.conf10
-rw-r--r--tests/topotests/bgp_remove_private_as/r2/bgpd.conf22
-rw-r--r--tests/topotests/bgp_remove_private_as/r2/zebra.conf14
-rw-r--r--tests/topotests/bgp_remove_private_as/r3/bgpd.conf19
-rw-r--r--tests/topotests/bgp_remove_private_as/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_remove_private_as/r4/bgpd.conf19
-rw-r--r--tests/topotests/bgp_remove_private_as/r4/zebra.conf10
-rw-r--r--tests/topotests/bgp_remove_private_as/r5/bgpd.conf22
-rw-r--r--tests/topotests/bgp_remove_private_as/r5/zebra.conf14
-rw-r--r--tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py430
-rwxr-xr-xtests/topotests/bgp_rfapi_basic_sanity/__init__.py0
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/customize.py115
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf50
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf12
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf24
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf33
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf19
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf27
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf48
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf17
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf29
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf49
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf12
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf23
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py159
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py50
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py102
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py74
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py325
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py124
-rwxr-xr-xtests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py97
-rwxr-xr-xtests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py0
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf51
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf33
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf49
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf1
-rw-r--r--tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf50
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/scripts1
l---------tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py1
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/__init__.py0
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py135
-rw-r--r--tests/topotests/bgp_roles_capability/__init__.py0
-rw-r--r--tests/topotests/bgp_roles_capability/r1/bgpd.conf25
-rw-r--r--tests/topotests/bgp_roles_capability/r1/zebra.conf18
-rw-r--r--tests/topotests/bgp_roles_capability/r2/bgpd.conf5
-rw-r--r--tests/topotests/bgp_roles_capability/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r3/bgpd.conf4
-rw-r--r--tests/topotests/bgp_roles_capability/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r4/bgpd.conf3
-rw-r--r--tests/topotests/bgp_roles_capability/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r5/bgpd.conf3
-rw-r--r--tests/topotests/bgp_roles_capability/r5/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/r6/bgpd.conf4
-rw-r--r--tests/topotests/bgp_roles_capability/r6/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_capability/roles_capability_stand.dot15
-rw-r--r--tests/topotests/bgp_roles_capability/roles_capability_stand.jpgbin0 -> 22695 bytes
-rw-r--r--tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py185
-rw-r--r--tests/topotests/bgp_roles_filtering/__init__.py0
-rw-r--r--tests/topotests/bgp_roles_filtering/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r10/bgpd.conf21
-rw-r--r--tests/topotests/bgp_roles_filtering/r10/zebra.conf24
-rw-r--r--tests/topotests/bgp_roles_filtering/r2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_roles_filtering/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r4/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r4/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r5/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r5/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r6/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r6/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/r7/bgpd.conf11
-rw-r--r--tests/topotests/bgp_roles_filtering/r7/zebra.conf6
-rw-r--r--tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot21
-rw-r--r--tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpgbin0 -> 54044 bytes
-rw-r--r--tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py144
-rw-r--r--tests/topotests/bgp_route_aggregation/bgp_aggregation.json249
-rw-r--r--tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py1173
-rw-r--r--tests/topotests/bgp_route_map/bgp_route_map_topo1.json187
-rwxr-xr-xtests/topotests/bgp_route_map/bgp_route_map_topo2.json316
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo1.py1409
-rw-r--r--tests/topotests/bgp_route_map/test_route_map_topo2.py3981
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf24
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf4
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf4
-rw-r--r--tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py120
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py0
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf34
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf4
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf35
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf11
-rw-r--r--tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py121
-rw-r--r--tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf2
-rw-r--r--tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py129
-rw-r--r--tests/topotests/bgp_route_server_client/__init__.py0
-rw-r--r--tests/topotests/bgp_route_server_client/r1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_route_server_client/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_route_server_client/r2/bgpd.conf17
-rw-r--r--tests/topotests/bgp_route_server_client/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_route_server_client/r3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_route_server_client/r3/zebra.conf7
-rw-r--r--tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py143
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf11
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref144
-rw-r--r--tests/topotests/bgp_rr_ibgp/spine1/zebra.conf9
-rw-r--r--tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py190
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref157
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor1/zebra.conf12
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref157
-rw-r--r--tests/topotests/bgp_rr_ibgp/tor2/zebra.conf13
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/__init__.py0
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf14
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf11
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf6
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py143
-rw-r--r--tests/topotests/bgp_set_aspath_replace/__init__.py0
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf17
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_set_aspath_replace/r3/zebra.conf10
-rw-r--r--tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py103
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/__init__.py0
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf15
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf15
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py130
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf12
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf19
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf12
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf19
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf12
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf19
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf12
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf19
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf48
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf46
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf20
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf33
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf37
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf24
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf45
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf27
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf43
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf36
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf18
-rw-r--r--tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf27
-rwxr-xr-xtests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py751
-rw-r--r--tests/topotests/bgp_soo/__init__.py0
-rw-r--r--tests/topotests/bgp_soo/cpe1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_soo/cpe1/zebra.conf12
-rw-r--r--tests/topotests/bgp_soo/cpe2/bgpd.conf10
-rw-r--r--tests/topotests/bgp_soo/cpe2/zebra.conf9
-rw-r--r--tests/topotests/bgp_soo/pe1/bgpd.conf27
-rw-r--r--tests/topotests/bgp_soo/pe1/ldpd.conf10
-rw-r--r--tests/topotests/bgp_soo/pe1/ospfd.conf7
-rw-r--r--tests/topotests/bgp_soo/pe1/zebra.conf12
-rw-r--r--tests/topotests/bgp_soo/pe2/bgpd.conf31
-rw-r--r--tests/topotests/bgp_soo/pe2/ldpd.conf10
-rw-r--r--tests/topotests/bgp_soo/pe2/ospfd.conf7
-rw-r--r--tests/topotests/bgp_soo/pe2/zebra.conf12
-rw-r--r--tests/topotests/bgp_soo/test_bgp_soo.py186
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf4
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf6
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf0
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf5
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf9
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf52
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf3
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf29
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf52
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf3
-rw-r--r--tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf29
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py128
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf66
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json160
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf42
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf67
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json170
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json93
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json169
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf42
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py304
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf8
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json58
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf14
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf66
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf41
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf66
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json167
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json92
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json86
-rw-r--r--tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf40
-rwxr-xr-xtests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py171
-rw-r--r--tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json40
-rw-r--r--tests/topotests/bgp_suppress_fib/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_suppress_fib/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json68
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf18
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/bgpd.conf11
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json1
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/v4_override.json20
-rw-r--r--tests/topotests/bgp_suppress_fib/r2/zebra.conf16
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/bgpd.conf9
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/v4_override.json4
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/v4_route.json28
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/v4_route2.json4
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/v4_route3.json23
-rw-r--r--tests/topotests/bgp_suppress_fib/r3/zebra.conf6
-rw-r--r--tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py235
-rw-r--r--tests/topotests/bgp_tcp_mss/__init__.py0
-rw-r--r--tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json222
-rw-r--r--tests/topotests/bgp_tcp_mss/r1/bgpd.conf6
-rw-r--r--tests/topotests/bgp_tcp_mss/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_tcp_mss/r2/bgpd.conf6
-rw-r--r--tests/topotests/bgp_tcp_mss/r2/zebra.conf6
-rw-r--r--tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py175
-rw-r--r--tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py766
-rw-r--r--tests/topotests/bgp_update_delay/__init__.py0
-rw-r--r--tests/topotests/bgp_update_delay/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_update_delay/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_update_delay/r2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_update_delay/r2/zebra.conf20
-rw-r--r--tests/topotests/bgp_update_delay/r3/bgpd.conf10
-rw-r--r--tests/topotests/bgp_update_delay/r3/zebra.conf9
-rw-r--r--tests/topotests/bgp_update_delay/r4/bgpd.conf11
-rw-r--r--tests/topotests/bgp_update_delay/r4/zebra.conf9
-rw-r--r--tests/topotests/bgp_update_delay/r5/bgpd.conf11
-rw-r--r--tests/topotests/bgp_update_delay/r5/zebra.conf9
-rw-r--r--tests/topotests/bgp_update_delay/test_bgp_update_delay.py308
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json50
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf25
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf7
-rw-r--r--tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py187
-rw-r--r--tests/topotests/bgp_vpnv4_gre/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf26
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json50
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r1/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json38
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf22
-rw-r--r--tests/topotests/bgp_vpnv4_gre/r2/zebra.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py191
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/__init__.py0
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf24
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json69
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json94
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf13
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf35
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf14
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf16
-rw-r--r--tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py201
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json563
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json563
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py1916
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py949
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json1088
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py1803
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json1088
-rw-r--r--tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py1909
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf23
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf13
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf41
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json32
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json32
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json34
-rw-r--r--tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf10
-rwxr-xr-xtests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py121
-rw-r--r--tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py0
-rw-r--r--tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf31
-rw-r--r--tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf6
-rw-r--r--tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py104
-rw-r--r--tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json563
-rw-r--r--tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json1088
-rw-r--r--tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py916
-rw-r--r--tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py539
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py0
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf15
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json44
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json37
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf9
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf18
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json44
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json23
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf9
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot44
-rw-r--r--tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py168
-rw-r--r--tests/topotests/bgp_vrf_netns/__init__.py0
-rw-r--r--tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot50
-rw-r--r--tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdfbin0 -> 13104 bytes
-rw-r--r--tests/topotests/bgp_vrf_netns/exabgp.env54
-rwxr-xr-xtests/topotests/bgp_vrf_netns/peer1/exa-send.py28
-rw-r--r--tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg21
-rw-r--r--tests/topotests/bgp_vrf_netns/r1/bgpd.conf10
-rw-r--r--tests/topotests/bgp_vrf_netns/r1/summary.txt17
-rw-r--r--tests/topotests/bgp_vrf_netns/r1/summary20.txt15
-rw-r--r--tests/topotests/bgp_vrf_netns/r1/zebra.conf7
-rw-r--r--tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py225
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf16
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf18
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs16
-rw-r--r--tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py149
-rw-r--r--tests/topotests/config_timing/r1/staticd.conf1
-rw-r--r--tests/topotests/config_timing/r1/zebra.conf18
-rw-r--r--tests/topotests/config_timing/test_config_timing.py215
-rwxr-xr-xtests/topotests/conftest.py537
-rw-r--r--tests/topotests/cspf_topo1/r1/isisd.conf33
-rw-r--r--tests/topotests/cspf_topo1/r1/sharpd.conf3
-rw-r--r--tests/topotests/cspf_topo1/r1/zebra.conf27
-rw-r--r--tests/topotests/cspf_topo1/r2/isisd.conf46
-rw-r--r--tests/topotests/cspf_topo1/r2/zebra.conf45
-rw-r--r--tests/topotests/cspf_topo1/r3/isisd.conf34
-rw-r--r--tests/topotests/cspf_topo1/r3/zebra.conf27
-rw-r--r--tests/topotests/cspf_topo1/r4/isisd.conf40
-rw-r--r--tests/topotests/cspf_topo1/r4/zebra.conf26
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt1
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-failed-same.txt1
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-failed-src.txt1
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-failed.txt1
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt3
-rw-r--r--tests/topotests/cspf_topo1/reference/sharp-ted.json858
-rw-r--r--tests/topotests/cspf_topo1/test_cspf_topo1.py253
-rw-r--r--tests/topotests/docker/README.md72
-rwxr-xr-xtests/topotests/docker/build.sh30
-rwxr-xr-xtests/topotests/docker/frr-topotests.sh174
-rwxr-xr-xtests/topotests/docker/inner/compile_frr.sh106
-rwxr-xr-xtests/topotests/docker/inner/entrypoint.sh50
-rwxr-xr-xtests/topotests/docker/inner/funcs.sh72
-rw-r--r--tests/topotests/docker/inner/motd.txt15
-rwxr-xr-xtests/topotests/docker/inner/openvswitch.sh59
-rw-r--r--tests/topotests/eigrp_topo1/r1/eigrpd.conf8
-rw-r--r--tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json32
-rw-r--r--tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref14
-rw-r--r--tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref90
-rw-r--r--tests/topotests/eigrp_topo1/r1/zebra.conf20
-rw-r--r--tests/topotests/eigrp_topo1/r2/eigrpd.conf7
-rw-r--r--tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json32
-rw-r--r--tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref14
-rw-r--r--tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref90
-rw-r--r--tests/topotests/eigrp_topo1/r2/zebra.conf21
-rw-r--r--tests/topotests/eigrp_topo1/r3/eigrpd.conf6
-rw-r--r--tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json32
-rw-r--r--tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref14
-rw-r--r--tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref108
-rw-r--r--tests/topotests/eigrp_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/eigrp_topo1/test_eigrp_topo1.dot62
-rw-r--r--tests/topotests/eigrp_topo1/test_eigrp_topo1.py292
-rw-r--r--tests/topotests/evpn_pim_1/host1/bgpd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/host1/pimd.conf4
-rw-r--r--tests/topotests/evpn_pim_1/host1/zebra.conf5
-rw-r--r--tests/topotests/evpn_pim_1/host2/bgpd.conf1
-rw-r--r--tests/topotests/evpn_pim_1/host2/pimd.conf4
-rw-r--r--tests/topotests/evpn_pim_1/host2/zebra.conf5
-rw-r--r--tests/topotests/evpn_pim_1/leaf1/bgpd.conf11
-rw-r--r--tests/topotests/evpn_pim_1/leaf1/pimd.conf16
-rw-r--r--tests/topotests/evpn_pim_1/leaf1/zebra.conf6
-rw-r--r--tests/topotests/evpn_pim_1/leaf2/bgpd.conf11
-rw-r--r--tests/topotests/evpn_pim_1/leaf2/pimd.conf14
-rw-r--r--tests/topotests/evpn_pim_1/leaf2/zebra.conf6
-rw-r--r--tests/topotests/evpn_pim_1/spine/bgp.summ.json41
-rw-r--r--tests/topotests/evpn_pim_1/spine/bgpd.conf13
-rw-r--r--tests/topotests/evpn_pim_1/spine/join-info.json34
-rw-r--r--tests/topotests/evpn_pim_1/spine/pimd.conf14
-rw-r--r--tests/topotests/evpn_pim_1/spine/zebra.conf8
-rw-r--r--tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py217
-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.json1004
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py1021
-rw-r--r--tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py2316
-rwxr-xr-xtests/topotests/example_test/__init__.py0
-rw-r--r--tests/topotests/example_test/r1/zebra.conf8
-rw-r--r--tests/topotests/example_test/r2/zebra.conf4
-rwxr-xr-xtests/topotests/example_test/test_example.py70
-rw-r--r--tests/topotests/example_test/test_template.dot51
-rw-r--r--tests/topotests/example_test/test_template.jpgbin0 -> 15470 bytes
-rw-r--r--tests/topotests/example_test/test_template.py173
-rw-r--r--tests/topotests/example_test/test_template_json.json188
-rw-r--r--tests/topotests/example_test/test_template_json.py68
-rwxr-xr-xtests/topotests/example_topojson_test/__init__.py0
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_multiple_links/__init__.py0
-rw-r--r--tests/topotests/example_topojson_test/test_topo_json_multiple_links/example_topojson_multiple_links.json152
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py197
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_single_link/__init__.py0
-rw-r--r--tests/topotests/example_topojson_test/test_topo_json_single_link/example_topojson.json153
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py197
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_single_link_loopback/__init__.py0
-rw-r--r--tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/example_topojson.json161
-rwxr-xr-xtests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py208
l---------tests/topotests/grpc_basic/lib1
-rw-r--r--tests/topotests/grpc_basic/r1/zebra.conf8
-rw-r--r--tests/topotests/grpc_basic/r2/zebra.conf8
-rw-r--r--tests/topotests/grpc_basic/test_basic_grpc.py179
-rw-r--r--tests/topotests/isis_lfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/isisd.conf52
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step1/show_ipv6_route.ref236
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref101
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step10/show_ipv6_route.ref.diff46
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step11/show_ipv6_route.ref.diff23
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step12/show_ipv6_route.ref.diff107
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step13/show_ipv6_route.ref.diff45
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step2/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step3/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step4/show_ipv6_route.ref.diff142
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step5/show_ipv6_route.ref.diff142
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step6/show_ipv6_route.ref.diff164
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step7/show_ipv6_route.ref.diff37
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step8/show_ipv6_route.ref.diff129
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/step9/show_ipv6_route.ref.diff46
-rw-r--r--tests/topotests/isis_lfa_topo1/rt1/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/isisd.conf39
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/step1/show_ipv6_route.ref162
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_lfa_topo1/rt2/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt3/isisd.conf38
-rw-r--r--tests/topotests/isis_lfa_topo1/rt3/step1/show_ipv6_route.ref188
-rw-r--r--tests/topotests/isis_lfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_lfa_topo1/rt3/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt4/isisd.conf32
-rw-r--r--tests/topotests/isis_lfa_topo1/rt4/step1/show_ipv6_route.ref172
-rw-r--r--tests/topotests/isis_lfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_lfa_topo1/rt4/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt5/isisd.conf32
-rw-r--r--tests/topotests/isis_lfa_topo1/rt5/step1/show_ipv6_route.ref176
-rw-r--r--tests/topotests/isis_lfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_lfa_topo1/rt5/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt6/isisd.conf31
-rw-r--r--tests/topotests/isis_lfa_topo1/rt6/step1/show_ipv6_route.ref172
-rw-r--r--tests/topotests/isis_lfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_lfa_topo1/rt6/zebra.conf16
-rw-r--r--tests/topotests/isis_lfa_topo1/rt7/isisd.conf51
-rw-r--r--tests/topotests/isis_lfa_topo1/rt7/step1/show_ipv6_route.ref186
-rw-r--r--tests/topotests/isis_lfa_topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref101
-rw-r--r--tests/topotests/isis_lfa_topo1/rt7/zebra.conf16
-rwxr-xr-xtests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py631
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf27
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ip_route.ref89
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ipv6_route.ref65
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ip_route.ref82
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ipv6_route.ref59
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ip_route.ref62
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ip_route.ref89
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ipv6_route.ref65
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf35
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ip_route.ref77
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref58
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf35
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ip_route.ref97
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ipv6_route.ref59
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref51
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf42
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ip_route.ref94
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ipv6_route.ref21
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf25
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf42
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ip_route.ref116
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf25
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf32
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ip_route.ref106
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ipv6_route.ref46
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ip_route.ref99
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ipv6_route.ref40
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ip_route.ref79
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ipv6_route.ref21
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ip_route.ref106
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ipv6_route.ref46
-rw-r--r--tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf22
-rwxr-xr-xtests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py377
-rw-r--r--tests/topotests/isis_rlfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/isisd.conf39
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step1/show_ip_route.ref235
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref207
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step10/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step10/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step2/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step2/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step3/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step3/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step4/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step4/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step5/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step5/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step6/show_ip_route.ref.diff134
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step6/show_ipv6_route.ref.diff122
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step7/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step7/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step8/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step8/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step9/show_ip_route.ref.diff68
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/step9/show_ipv6_route.ref.diff62
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt1/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt2/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt3/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt3/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt4/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt5/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt5/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt6/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt7/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt7/zebra.conf22
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt8/isisd.conf32
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf30
-rw-r--r--tests/topotests/isis_rlfa_topo1/rt8/zebra.conf22
-rwxr-xr-xtests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py657
-rw-r--r--tests/topotests/isis_snmp/ce3/zebra.conf12
-rw-r--r--tests/topotests/isis_snmp/r1/isisd.conf24
-rw-r--r--tests/topotests/isis_snmp/r1/ldpd.conf26
-rw-r--r--tests/topotests/isis_snmp/r1/show_ip_route.ref143
-rw-r--r--tests/topotests/isis_snmp/r1/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis_snmp/r1/snmpd.conf18
-rw-r--r--tests/topotests/isis_snmp/r1/zebra.conf24
-rw-r--r--tests/topotests/isis_snmp/r2/isisd.conf25
-rw-r--r--tests/topotests/isis_snmp/r2/ldpd.conf25
-rw-r--r--tests/topotests/isis_snmp/r2/show_ip_route.ref143
-rw-r--r--tests/topotests/isis_snmp/r2/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis_snmp/r2/snmpd.conf18
-rw-r--r--tests/topotests/isis_snmp/r2/zebra.conf24
-rw-r--r--tests/topotests/isis_snmp/r3/isisd.conf25
-rw-r--r--tests/topotests/isis_snmp/r3/ldpd.conf25
-rw-r--r--tests/topotests/isis_snmp/r3/show_ip_route.ref143
-rw-r--r--tests/topotests/isis_snmp/r3/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis_snmp/r3/snmpd.conf18
-rw-r--r--tests/topotests/isis_snmp/r3/zebra.conf28
-rw-r--r--tests/topotests/isis_snmp/r4/isisd.conf24
-rw-r--r--tests/topotests/isis_snmp/r4/ldpd.conf25
-rw-r--r--tests/topotests/isis_snmp/r4/show_ip_route.ref143
-rw-r--r--tests/topotests/isis_snmp/r4/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis_snmp/r4/snmpd.conf18
-rw-r--r--tests/topotests/isis_snmp/r4/zebra.conf24
-rw-r--r--tests/topotests/isis_snmp/r5/isisd.conf25
-rw-r--r--tests/topotests/isis_snmp/r5/ldpd.conf25
-rw-r--r--tests/topotests/isis_snmp/r5/ldpdconf25
-rw-r--r--tests/topotests/isis_snmp/r5/show_ip_route.ref143
-rw-r--r--tests/topotests/isis_snmp/r5/show_yang_interface_isis_adjacencies.ref40
-rw-r--r--tests/topotests/isis_snmp/r5/snmpd.conf18
-rw-r--r--tests/topotests/isis_snmp/r5/zebra.conf24
-rw-r--r--tests/topotests/isis_snmp/test_isis_snmp.dot114
-rwxr-xr-xtests/topotests/isis_snmp/test_isis_snmp.py374
-rw-r--r--tests/topotests/isis_sr_te_topo1/dst/zebra.conf19
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf16
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/isisd.conf30
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/pathd.conf21
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_with_candidate.ref91
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_without_candidate.ref74
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref25
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_add_segment.ref21
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_change_segment.ref21
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_active_srte.ref29
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_empty.ref2
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref38
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_active.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_inactive.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt2/isisd.conf41
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt2/zebra.conf25
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt3/isisd.conf41
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt3/zebra.conf25
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt4/isisd.conf48
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt4/zebra.conf28
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt5/isisd.conf48
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt5/zebra.conf28
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf12
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/isisd.conf36
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/pathd.conf21
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_with_candidate.ref91
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_without_candidate.ref74
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref19
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref19
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref23
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/step4/show_mpls_table.ref20
-rw-r--r--tests/topotests/isis_sr_te_topo1/rt6/zebra.conf27
-rwxr-xr-xtests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py870
-rw-r--r--tests/topotests/isis_sr_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/isisd.conf31
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step1/show_ip_route.ref327
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step1/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step1/show_mpls_table.ref170
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step10/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step10/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step10/show_mpls_table.ref192
l---------tests/topotests/isis_sr_topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step2/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step2/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step2/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step3/show_ip_route.ref287
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step3/show_ipv6_route.ref121
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step3/show_mpls_table.ref134
l---------tests/topotests/isis_sr_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step4/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step4/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step4/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step5/show_ip_route.ref314
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step5/show_ipv6_route.ref146
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step5/show_mpls_table.ref134
l---------tests/topotests/isis_sr_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step6/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step6/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step6/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step7/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step7/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step7/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step8/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step8/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step8/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step9/show_ip_route.ref320
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step9/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/step9/show_mpls_table.ref192
l---------tests/topotests/isis_sr_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/isisd.conf42
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step1/show_ip_route.ref380
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step1/show_ipv6_route.ref179
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step1/show_mpls_table.ref228
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step10/show_ip_route.ref282
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step10/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step10/show_mpls_table.ref186
l---------tests/topotests/isis_sr_topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step2/show_ip_route.ref353
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step2/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step2/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step3/show_ip_route.ref306
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step3/show_ipv6_route.ref130
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step3/show_mpls_table.ref168
l---------tests/topotests/isis_sr_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step4/show_ip_route.ref353
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step4/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step4/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step5/show_ip_route.ref347
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step5/show_ipv6_route.ref155
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step5/show_mpls_table.ref168
l---------tests/topotests/isis_sr_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step6/show_ip_route.ref353
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step6/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step6/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step7/show_ip_route.ref350
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step7/show_ipv6_route.ref158
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step7/show_mpls_table.ref180
l---------tests/topotests/isis_sr_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step8/show_ip_route.ref353
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step8/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step8/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step9/show_ip_route.ref353
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step9/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/step9/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt2/zebra.conf25
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/isisd.conf42
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step1/show_ip_route.ref380
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step1/show_ipv6_route.ref179
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step1/show_mpls_table.ref228
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step10/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step10/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step10/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step2/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step2/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step2/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step3/show_ip_route.ref313
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step3/show_ipv6_route.ref130
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step3/show_mpls_table.ref168
l---------tests/topotests/isis_sr_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step4/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step4/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step4/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step5/show_ip_route.ref354
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step5/show_ipv6_route.ref155
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step5/show_mpls_table.ref168
l---------tests/topotests/isis_sr_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step6/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step6/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step6/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step7/show_ip_route.ref357
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step7/show_ipv6_route.ref158
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step7/show_mpls_table.ref180
l---------tests/topotests/isis_sr_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step8/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step8/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step8/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step9/show_ip_route.ref360
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step9/show_ipv6_route.ref161
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/step9/show_mpls_table.ref204
l---------tests/topotests/isis_sr_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt3/zebra.conf25
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/isisd.conf51
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step1/show_ip_route.ref342
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step1/show_ipv6_route.ref148
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step1/show_mpls_table.ref215
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step10/show_ip_route.ref297
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step10/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step10/show_mpls_table.ref185
l---------tests/topotests/isis_sr_topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step2/show_ip_route.ref355
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step2/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step2/show_mpls_table.ref203
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step3/show_ip_route.ref336
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step3/show_ipv6_route.ref126
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step3/show_mpls_table.ref197
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step4/show_ip_route.ref355
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step4/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step4/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step5/show_ip_route.ref347
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step5/show_ipv6_route.ref133
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step5/show_mpls_table.ref143
l---------tests/topotests/isis_sr_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step6/show_ip_route.ref355
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step6/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step6/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step7/show_ip_route.ref349
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step7/show_ipv6_route.ref133
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step7/show_mpls_table.ref167
l---------tests/topotests/isis_sr_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step8/show_ip_route.ref355
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step8/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step8/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step9/show_ip_route.ref355
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step9/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/step9/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt4/zebra.conf30
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/isisd.conf51
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step1/show_ip_route.ref342
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step1/show_ipv6_route.ref148
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step1/show_mpls_table.ref215
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step10/show_ip_route.ref331
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step10/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step10/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step2/show_ip_route.ref338
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step2/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step2/show_mpls_table.ref203
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step3/show_ip_route.ref312
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step3/show_ipv6_route.ref126
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step3/show_mpls_table.ref197
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step4/show_ip_route.ref323
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step4/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step4/show_mpls_table.ref203
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref63
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step5/show_ip_route.ref315
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step5/show_ipv6_route.ref133
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step5/show_mpls_table.ref143
l---------tests/topotests/isis_sr_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step6/show_ip_route.ref323
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step6/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step6/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step7/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step7/show_ipv6_route.ref133
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step7/show_mpls_table.ref167
l---------tests/topotests/isis_sr_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step8/show_ip_route.ref323
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step8/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step8/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step9/show_ip_route.ref323
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step9/show_ipv6_route.ref139
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/step9/show_mpls_table.ref203
l---------tests/topotests/isis_sr_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt5/zebra.conf30
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/isisd.conf37
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step1/show_ip_route.ref324
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step1/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step1/show_mpls_table.ref170
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step10/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step10/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step10/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step2/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step2/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step2/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step3/show_ip_route.ref2
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step3/show_ipv6_route.ref2
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step3/show_mpls_table.ref2
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref3
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step4/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step4/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step4/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref293
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref128
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref2
l---------tests/topotests/isis_sr_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step6/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step6/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step6/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step7/show_ip_route.ref311
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step7/show_ipv6_route.ref146
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step7/show_mpls_table.ref134
l---------tests/topotests/isis_sr_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step8/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step8/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step8/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step9/show_ip_route.ref317
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step9/show_ipv6_route.ref152
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/step9/show_mpls_table.ref170
l---------tests/topotests/isis_sr_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref1
-rw-r--r--tests/topotests/isis_sr_topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py1086
-rwxr-xr-xtests/topotests/isis_te_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_te_topo1/r1/isisd.conf31
-rw-r--r--tests/topotests/isis_te_topo1/r1/zebra.conf25
-rw-r--r--tests/topotests/isis_te_topo1/r2/isisd.conf45
-rw-r--r--tests/topotests/isis_te_topo1/r2/zebra.conf39
-rw-r--r--tests/topotests/isis_te_topo1/r3/isisd.conf34
-rw-r--r--tests/topotests/isis_te_topo1/r3/zebra.conf25
-rw-r--r--tests/topotests/isis_te_topo1/r4/isisd.conf39
-rw-r--r--tests/topotests/isis_te_topo1/r4/zebra.conf22
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step1.json840
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step2.json644
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step3.json744
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step4.json744
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step5.json940
-rw-r--r--tests/topotests/isis_te_topo1/reference/ted_step6.json941
-rw-r--r--tests/topotests/isis_te_topo1/test_isis_te_topo1.py265
-rw-r--r--tests/topotests/isis_tilfa_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/isisd.conf33
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step1/show_ip_route.ref294
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref121
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step1/show_mpls_table.ref134
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref32
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step2/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step2/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step2/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step3/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step3/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step3/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step4/show_ip_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step4/show_ipv6_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step4/show_mpls_table.ref.diff33
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step5/show_ip_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step5/show_ipv6_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step5/show_mpls_table.ref.diff33
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step6/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step6/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step6/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step7/show_ip_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step7/show_ipv6_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step7/show_mpls_table.ref.diff33
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step8/show_ip_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step8/show_ipv6_route.ref.diff14
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step8/show_mpls_table.ref.diff33
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step9/show_ip_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step9/show_ipv6_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/step9/show_mpls_table.ref.diff64
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt1/zebra.conf19
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/isisd.conf45
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step1/show_ip_route.ref563
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref229
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step1/show_mpls_table.ref286
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step2/show_ip_route.ref.diff169
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step2/show_ipv6_route.ref.diff72
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step2/show_mpls_table.ref.diff102
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step3/show_ip_route.ref.diff169
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step3/show_ipv6_route.ref.diff72
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step3/show_mpls_table.ref.diff102
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step4/show_ip_route.ref.diff192
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step4/show_ipv6_route.ref.diff146
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step4/show_mpls_table.ref.diff200
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step5/show_ip_route.ref.diff192
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step5/show_ipv6_route.ref.diff146
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step5/show_mpls_table.ref.diff200
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step6/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step6/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step6/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step7/show_ip_route.ref.diff288
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step7/show_ipv6_route.ref.diff139
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step7/show_mpls_table.ref.diff207
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step8/show_ip_route.ref.diff288
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step8/show_ipv6_route.ref.diff139
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step8/show_mpls_table.ref.diff207
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step9/show_ip_route.ref.diff119
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step9/show_ipv6_route.ref.diff74
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/step9/show_mpls_table.ref.diff182
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt2/zebra.conf25
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/isisd.conf45
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step1/show_ip_route.ref563
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref229
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step1/show_mpls_table.ref286
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step2/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step2/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step2/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step3/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step3/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step3/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step4/show_ip_route.ref.diff288
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step4/show_ipv6_route.ref.diff139
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step4/show_mpls_table.ref.diff206
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step5/show_ip_route.ref.diff288
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step5/show_ipv6_route.ref.diff139
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step5/show_mpls_table.ref.diff206
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step6/show_ip_route.ref.diff101
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step6/show_ipv6_route.ref.diff83
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step6/show_mpls_table.ref.diff130
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step7/show_ip_route.ref.diff32
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step7/show_ipv6_route.ref.diff32
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step7/show_mpls_table.ref.diff71
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step8/show_ip_route.ref.diff32
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step8/show_ipv6_route.ref.diff32
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step8/show_mpls_table.ref.diff71
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step9/show_ip_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step9/show_ipv6_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/step9/show_mpls_table.ref.diff133
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt3/zebra.conf25
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/isisd.conf53
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step1/show_ip_route.ref506
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref207
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step1/show_mpls_table.ref262
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step2/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step2/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step2/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step3/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step3/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step3/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff367
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff161
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff265
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff367
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff161
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff265
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step6/show_ip_route.ref.diff56
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step6/show_ipv6_route.ref.diff38
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step6/show_mpls_table.ref.diff74
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step7/show_ip_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step7/show_ipv6_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step7/show_mpls_table.ref.diff53
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step8/show_ip_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step8/show_ipv6_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step8/show_mpls_table.ref.diff53
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step9/show_ip_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step9/show_ipv6_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/step9/show_mpls_table.ref.diff110
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt4/zebra.conf28
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/isisd.conf53
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step1/show_ip_route.ref506
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref207
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step1/show_mpls_table.ref262
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref82
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step2/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step2/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step2/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step3/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step3/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step3/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step4/show_ip_route.ref.diff161
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step4/show_ipv6_route.ref.diff95
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step4/show_mpls_table.ref.diff166
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step5/show_ip_route.ref.diff161
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step5/show_ipv6_route.ref.diff95
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step5/show_mpls_table.ref.diff166
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step6/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step6/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step6/show_mpls_table.ref.diff146
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step7/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step7/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step7/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step8/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step8/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step8/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step9/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step9/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/step9/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt5/zebra.conf28
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/isisd.conf39
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step1/show_ip_route.ref413
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref173
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step1/show_mpls_table.ref214
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref44
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step2/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step2/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step2/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step3/show_ip_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step3/show_ipv6_route.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step3/show_mpls_table.ref.diff0
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step4/show_ip_route.ref.diff70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step4/show_ipv6_route.ref.diff70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step4/show_mpls_table.ref.diff109
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step5/show_ip_route.ref.diff70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step5/show_ipv6_route.ref.diff70
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step5/show_mpls_table.ref.diff112
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step6/show_ip_route.ref.diff38
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step6/show_ipv6_route.ref.diff38
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step6/show_mpls_table.ref.diff74
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step7/show_ip_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step7/show_ipv6_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step7/show_mpls_table.ref.diff52
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step8/show_ip_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step8/show_ipv6_route.ref.diff24
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step8/show_mpls_table.ref.diff52
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step9/show_ip_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step9/show_ipv6_route.ref.diff11
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/step9/show_mpls_table.ref.diff39
-rw-r--r--tests/topotests/isis_tilfa_topo1/rt6/zebra.conf22
-rwxr-xr-xtests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py755
-rw-r--r--tests/topotests/isis_topo1/__init__.py0
-rw-r--r--tests/topotests/isis_topo1/r1/isisd.conf17
-rw-r--r--tests/topotests/isis_topo1/r1/r1_route.json68
-rw-r--r--tests/topotests/isis_topo1/r1/r1_route6.json66
-rw-r--r--tests/topotests/isis_topo1/r1/r1_route6_linux.json14
-rw-r--r--tests/topotests/isis_topo1/r1/r1_route_linux.json14
-rw-r--r--tests/topotests/isis_topo1/r1/r1_topology.json96
-rw-r--r--tests/topotests/isis_topo1/r1/zebra.conf9
-rw-r--r--tests/topotests/isis_topo1/r2/isisd.conf17
-rw-r--r--tests/topotests/isis_topo1/r2/r2_route.json68
-rw-r--r--tests/topotests/isis_topo1/r2/r2_route6.json66
-rw-r--r--tests/topotests/isis_topo1/r2/r2_route6_linux.json14
-rw-r--r--tests/topotests/isis_topo1/r2/r2_route_linux.json14
-rw-r--r--tests/topotests/isis_topo1/r2/r2_topology.json96
-rw-r--r--tests/topotests/isis_topo1/r2/zebra.conf9
-rw-r--r--tests/topotests/isis_topo1/r3/isisd.conf24
-rw-r--r--tests/topotests/isis_topo1/r3/r3_route.json150
-rw-r--r--tests/topotests/isis_topo1/r3/r3_route6.json132
-rw-r--r--tests/topotests/isis_topo1/r3/r3_route6_linux.json32
-rw-r--r--tests/topotests/isis_topo1/r3/r3_route_linux.json32
-rw-r--r--tests/topotests/isis_topo1/r3/r3_topology.json194
-rw-r--r--tests/topotests/isis_topo1/r3/zebra.conf13
-rw-r--r--tests/topotests/isis_topo1/r4/isisd.conf24
-rw-r--r--tests/topotests/isis_topo1/r4/r4_route.json65
-rw-r--r--tests/topotests/isis_topo1/r4/r4_route6.json131
-rw-r--r--tests/topotests/isis_topo1/r4/r4_route6_linux.json32
-rw-r--r--tests/topotests/isis_topo1/r4/r4_route_linux.json32
-rw-r--r--tests/topotests/isis_topo1/r4/r4_topology.json194
-rw-r--r--tests/topotests/isis_topo1/r4/zebra.conf13
-rw-r--r--tests/topotests/isis_topo1/r5/isisd.conf23
-rw-r--r--tests/topotests/isis_topo1/r5/r5_route.json145
-rw-r--r--tests/topotests/isis_topo1/r5/r5_route6.json115
-rw-r--r--tests/topotests/isis_topo1/r5/r5_route6_linux.json26
-rw-r--r--tests/topotests/isis_topo1/r5/r5_route_linux.json26
-rw-r--r--tests/topotests/isis_topo1/r5/r5_topology.json156
-rw-r--r--tests/topotests/isis_topo1/r5/zebra.conf13
-rw-r--r--tests/topotests/isis_topo1/test_isis_topo1.dot100
-rw-r--r--tests/topotests/isis_topo1/test_isis_topo1.jpgbin0 -> 74340 bytes
-rw-r--r--tests/topotests/isis_topo1/test_isis_topo1.py497
-rw-r--r--tests/topotests/isis_topo1_vrf/__init__.py0
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r1/isisd.conf16
-rw-r--r--tests/topotests/isis_topo1_vrf/r1/r1_route.json37
-rw-r--r--tests/topotests/isis_topo1_vrf/r1/r1_route6.json36
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r1/r1_route6_linux.json14
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r1/r1_route_linux.json13
-rw-r--r--tests/topotests/isis_topo1_vrf/r1/r1_topology.json80
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r1/zebra.conf9
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r2/isisd.conf16
-rw-r--r--tests/topotests/isis_topo1_vrf/r2/r2_route.json37
-rw-r--r--tests/topotests/isis_topo1_vrf/r2/r2_route6.json36
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r2/r2_route6_linux.json14
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r2/r2_route_linux.json13
-rw-r--r--tests/topotests/isis_topo1_vrf/r2/r2_topology.json80
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r2/zebra.conf9
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r3/isisd.conf23
-rw-r--r--tests/topotests/isis_topo1_vrf/r3/r3_route.json86
-rw-r--r--tests/topotests/isis_topo1_vrf/r3/r3_route6.json70
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r3/r3_route6_linux.json26
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r3/r3_route_linux.json24
-rw-r--r--tests/topotests/isis_topo1_vrf/r3/r3_topology.json132
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r3/zebra.conf13
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r4/isisd.conf26
-rw-r--r--tests/topotests/isis_topo1_vrf/r4/r4_route.json81
-rw-r--r--tests/topotests/isis_topo1_vrf/r4/r4_route6.json70
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r4/r4_route6_linux.json26
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r4/r4_route_linux.json24
-rw-r--r--tests/topotests/isis_topo1_vrf/r4/r4_topology.json132
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r4/zebra.conf13
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r5/isisd.conf22
-rw-r--r--tests/topotests/isis_topo1_vrf/r5/r5_route.json94
-rw-r--r--tests/topotests/isis_topo1_vrf/r5/r5_route6.json70
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r5/r5_route6_linux.json26
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r5/r5_route_linux.json24
-rw-r--r--tests/topotests/isis_topo1_vrf/r5/r5_topology.json124
-rwxr-xr-xtests/topotests/isis_topo1_vrf/r5/zebra.conf13
-rwxr-xr-xtests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.dot100
-rwxr-xr-xtests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.jpgbin0 -> 74340 bytes
-rw-r--r--tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py441
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf25
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf12
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json12
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ip_route.ref160
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref61
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_binding.ref55
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_discovery.ref11
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref10
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf17
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf28
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf17
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json28
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ip_route.ref198
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref63
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_binding.ref63
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf27
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf24
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf13
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json20
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ip_route.ref198
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref61
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_binding.ref62
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_discovery.ref11
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref10
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf24
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf12
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json21
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ip_route.ref186
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref68
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_binding.ref68
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_discovery.ref2
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref2
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf17
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot76
-rw-r--r--tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py258
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/ldpd.conf24
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/ospfd.conf12
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json12
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref160
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/show_ldp_binding.ref61
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/show_ldp_discovery.ref11
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/show_ldp_neighbor.ref10
-rw-r--r--tests/topotests/ldp_oc_topo1/r1/zebra.conf17
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/ldpd.conf28
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/ospfd.conf17
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json28
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref198
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/show_ldp_binding.ref63
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_oc_topo1/r2/zebra.conf27
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/ldpd.conf24
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/ospfd.conf13
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json20
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref198
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/show_ldp_binding.ref61
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/show_ldp_discovery.ref11
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/show_ldp_neighbor.ref10
-rw-r--r--tests/topotests/ldp_oc_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/ldpd.conf24
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/ospfd.conf12
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json21
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref186
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/show_ldp_binding.ref68
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/show_ldp_discovery.ref2
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/show_ldp_neighbor.ref2
-rw-r--r--tests/topotests/ldp_oc_topo1/r4/zebra.conf17
-rw-r--r--tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot76
-rw-r--r--tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py238
-rw-r--r--tests/topotests/ldp_snmp/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp_snmp/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp_snmp/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp_snmp/r1/isisd.conf27
-rw-r--r--tests/topotests/ldp_snmp/r1/ldpd.conf35
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_snmp/r1/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_snmp/r1/snmpd.conf18
-rw-r--r--tests/topotests/ldp_snmp/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp_snmp/r2/isisd.conf28
-rw-r--r--tests/topotests/ldp_snmp/r2/ldpd.conf35
-rw-r--r--tests/topotests/ldp_snmp/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_snmp/r2/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_snmp/r2/snmpd.conf18
-rw-r--r--tests/topotests/ldp_snmp/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp_snmp/r3/isisd.conf29
-rw-r--r--tests/topotests/ldp_snmp/r3/ldpd.conf27
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_snmp/r3/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_snmp/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py385
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf27
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf33
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf28
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf33
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf29
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf25
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ip_route.ref134
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref13
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/show_yang_interface_isis_adjacencies.ref42
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.dot111
-rw-r--r--tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py626
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf33
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt0
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf20
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref8
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref7
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf33
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref8
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref7
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf25
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf18
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref11
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref12
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf32
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.dot111
-rw-r--r--tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py443
-rw-r--r--tests/topotests/ldp_topo1/r1/ip_mpls_route.ref6
-rw-r--r--tests/topotests/ldp_topo1/r1/ldpd.conf23
-rw-r--r--tests/topotests/ldp_topo1/r1/ospfd.conf11
-rw-r--r--tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json14
-rw-r--r--tests/topotests/ldp_topo1/r1/show_ipv4_route.ref7
-rw-r--r--tests/topotests/ldp_topo1/r1/show_mpls_ldp_binding.ref8
-rw-r--r--tests/topotests/ldp_topo1/r1/show_mpls_ldp_discovery.ref2
-rw-r--r--tests/topotests/ldp_topo1/r1/show_mpls_ldp_interface.ref2
-rw-r--r--tests/topotests/ldp_topo1/r1/show_mpls_ldp_neighbor.ref2
-rw-r--r--tests/topotests/ldp_topo1/r1/show_mpls_table.ref8
-rw-r--r--tests/topotests/ldp_topo1/r1/zebra.conf17
-rw-r--r--tests/topotests/ldp_topo1/r2/ip_mpls_route.ref3
-rw-r--r--tests/topotests/ldp_topo1/r2/ldpd.conf25
-rw-r--r--tests/topotests/ldp_topo1/r2/ospfd.conf19
-rw-r--r--tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json42
-rw-r--r--tests/topotests/ldp_topo1/r2/show_ipv4_route.ref7
-rw-r--r--tests/topotests/ldp_topo1/r2/show_mpls_ldp_binding.ref22
-rw-r--r--tests/topotests/ldp_topo1/r2/show_mpls_ldp_discovery.ref4
-rw-r--r--tests/topotests/ldp_topo1/r2/show_mpls_ldp_interface.ref3
-rw-r--r--tests/topotests/ldp_topo1/r2/show_mpls_ldp_neighbor.ref4
-rw-r--r--tests/topotests/ldp_topo1/r2/show_mpls_table.ref7
-rw-r--r--tests/topotests/ldp_topo1/r2/zebra.conf27
-rw-r--r--tests/topotests/ldp_topo1/r3/ip_mpls_route.ref4
-rw-r--r--tests/topotests/ldp_topo1/r3/ldpd.conf23
-rw-r--r--tests/topotests/ldp_topo1/r3/ospfd.conf16
-rw-r--r--tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json32
-rw-r--r--tests/topotests/ldp_topo1/r3/show_ipv4_route.ref7
-rw-r--r--tests/topotests/ldp_topo1/r3/show_mpls_ldp_binding.ref15
-rw-r--r--tests/topotests/ldp_topo1/r3/show_mpls_ldp_discovery.ref3
-rw-r--r--tests/topotests/ldp_topo1/r3/show_mpls_ldp_interface.ref2
-rw-r--r--tests/topotests/ldp_topo1/r3/show_mpls_ldp_neighbor.ref3
-rw-r--r--tests/topotests/ldp_topo1/r3/show_mpls_table.ref10
-rw-r--r--tests/topotests/ldp_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ldp_topo1/r4/ip_mpls_route.ref5
-rw-r--r--tests/topotests/ldp_topo1/r4/ldpd.conf23
-rw-r--r--tests/topotests/ldp_topo1/r4/ospfd.conf11
-rw-r--r--tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json24
-rw-r--r--tests/topotests/ldp_topo1/r4/show_ipv4_route.ref7
-rw-r--r--tests/topotests/ldp_topo1/r4/show_mpls_ldp_binding.ref15
-rw-r--r--tests/topotests/ldp_topo1/r4/show_mpls_ldp_discovery.ref3
-rw-r--r--tests/topotests/ldp_topo1/r4/show_mpls_ldp_interface.ref2
-rw-r--r--tests/topotests/ldp_topo1/r4/show_mpls_ldp_neighbor.ref3
-rw-r--r--tests/topotests/ldp_topo1/r4/show_mpls_table.ref9
-rw-r--r--tests/topotests/ldp_topo1/r4/zebra.conf17
-rw-r--r--tests/topotests/ldp_topo1/test_ldp_topo1.py857
-rw-r--r--tests/topotests/ldp_vpls_topo1/__init__.py0
-rw-r--r--tests/topotests/ldp_vpls_topo1/ce1/zebra.conf12
-rw-r--r--tests/topotests/ldp_vpls_topo1/ce2/zebra.conf12
-rw-r--r--tests/topotests/ldp_vpls_topo1/ce3/zebra.conf12
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/ldpd.conf35
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt0
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/ospfd.conf17
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ip_route_after_link_down.ref20
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_vpls_topo1/r1/zebra.conf29
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/ldpd.conf35
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/ospfd.conf17
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_binding.ref16
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref8
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_ldp_discovery.ref25
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_vpls_topo1/r2/zebra.conf28
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/ldpd.conf27
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/ospfd.conf17
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json26
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_ip_route.ref147
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_binding.ref2
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref2
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_ldp_binding.ref44
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_ldp_discovery.ref18
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/show_ldp_neighbor.ref16
-rw-r--r--tests/topotests/ldp_vpls_topo1/r3/zebra.conf28
-rw-r--r--tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.dot111
-rw-r--r--tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.pdfbin0 -> 16693 bytes
-rw-r--r--tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py304
-rw-r--r--tests/topotests/lib/__init__.py0
-rw-r--r--tests/topotests/lib/bgp.py5564
-rw-r--r--tests/topotests/lib/bgprib.py177
-rw-r--r--tests/topotests/lib/common_config.py5130
-rwxr-xr-xtests/topotests/lib/exa-receive.py43
-rw-r--r--tests/topotests/lib/fixtures.py47
-rwxr-xr-xtests/topotests/lib/grpc-query.py155
-rw-r--r--tests/topotests/lib/ltemplate.py320
-rw-r--r--tests/topotests/lib/lutil.py465
-rwxr-xr-xtests/topotests/lib/mcast-tester.py139
-rw-r--r--tests/topotests/lib/micronet.py1005
-rw-r--r--tests/topotests/lib/micronet_cli.py319
-rw-r--r--tests/topotests/lib/micronet_compat.py266
-rw-r--r--tests/topotests/lib/ospf.py2491
-rw-r--r--tests/topotests/lib/pim.py3979
-rwxr-xr-xtests/topotests/lib/scapy_sendpkt.py61
-rwxr-xr-xtests/topotests/lib/send_bsr_packet.py58
-rw-r--r--tests/topotests/lib/snmptest.py155
-rw-r--r--tests/topotests/lib/test/__init__.py0
-rwxr-xr-xtests/topotests/lib/test/test_json.py641
-rwxr-xr-xtests/topotests/lib/test/test_run_and_expect.py79
-rwxr-xr-xtests/topotests/lib/test/test_version.py90
-rw-r--r--tests/topotests/lib/topogen.py1338
-rw-r--r--tests/topotests/lib/topojson.py420
-rw-r--r--tests/topotests/lib/topolog.py178
-rw-r--r--tests/topotests/lib/topotest.py2175
-rw-r--r--tests/topotests/msdp_mesh_topo1/__init__.py0
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/bgpd.conf7
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/ospfd.conf8
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/pimd.conf17
-rw-r--r--tests/topotests/msdp_mesh_topo1/r1/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/bgpd.conf10
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/ospfd.conf13
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/pimd.conf16
-rw-r--r--tests/topotests/msdp_mesh_topo1/r2/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/bgpd.conf7
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/ospfd.conf8
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/pimd.conf17
-rw-r--r--tests/topotests/msdp_mesh_topo1/r3/zebra.conf11
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot88
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.pngbin0 -> 35201 bytes
-rw-r--r--tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py242
-rw-r--r--tests/topotests/msdp_topo1/__init__.py0
-rw-r--r--tests/topotests/msdp_topo1/r1/bgpd.conf8
-rw-r--r--tests/topotests/msdp_topo1/r1/pimd.conf22
-rw-r--r--tests/topotests/msdp_topo1/r1/zebra.conf14
-rw-r--r--tests/topotests/msdp_topo1/r2/bgpd.conf8
-rw-r--r--tests/topotests/msdp_topo1/r2/pimd.conf18
-rw-r--r--tests/topotests/msdp_topo1/r2/zebra.conf11
-rw-r--r--tests/topotests/msdp_topo1/r3/bgpd.conf8
-rw-r--r--tests/topotests/msdp_topo1/r3/pimd.conf18
-rw-r--r--tests/topotests/msdp_topo1/r3/zebra.conf11
-rw-r--r--tests/topotests/msdp_topo1/r4/bgpd.conf9
-rw-r--r--tests/topotests/msdp_topo1/r4/pimd.conf22
-rw-r--r--tests/topotests/msdp_topo1/r4/zebra.conf14
-rwxr-xr-xtests/topotests/msdp_topo1/test_msdp_topo1.py454
-rw-r--r--tests/topotests/multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json238
-rw-r--r--tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py1787
-rw-r--r--tests/topotests/multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json238
-rw-r--r--tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py1119
-rw-r--r--tests/topotests/multicast_pim_dr_nondr_test/__init__.py0
-rw-r--r--tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json195
-rw-r--r--tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json85
-rw-r--r--tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json241
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py1139
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py940
-rwxr-xr-xtests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py829
-rw-r--r--tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json146
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py1608
-rw-r--r--tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json146
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py1843
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json146
-rw-r--r--tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json143
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py4789
-rwxr-xr-xtests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py1104
-rw-r--r--tests/topotests/multicast_pim_static_rp_topo1/__init__.py0
-rw-r--r--tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json119
-rw-r--r--tests/topotests/multicast_pim_static_rp_topo1/multicast_pimv6_static_rp.json197
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py1396
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py1424
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py1799
-rwxr-xr-xtests/topotests/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py414
-rw-r--r--tests/topotests/multicast_pim_uplink_topo1/multicast_pim_uplink_topo1.json226
-rw-r--r--tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py3327
-rw-r--r--tests/topotests/nhrp_topo/r1/nhrp4_cache.json29
-rw-r--r--tests/topotests/nhrp_topo/r1/nhrp_route4.json25
-rw-r--r--tests/topotests/nhrp_topo/r1/nhrpd.conf10
-rw-r--r--tests/topotests/nhrp_topo/r1/zebra.conf12
-rw-r--r--tests/topotests/nhrp_topo/r2/nhrp4_cache.json29
-rw-r--r--tests/topotests/nhrp_topo/r2/nhrp_route4.json25
-rw-r--r--tests/topotests/nhrp_topo/r2/nhrpd.conf10
-rw-r--r--tests/topotests/nhrp_topo/r2/zebra.conf12
-rw-r--r--tests/topotests/nhrp_topo/r3/zebra.conf11
-rw-r--r--tests/topotests/nhrp_topo/test_nhrp_topo.dot73
-rw-r--r--tests/topotests/nhrp_topo/test_nhrp_topo.py228
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf52
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r1/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf14
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r2/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf14
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r3/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf14
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r4/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf39
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r5/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf9
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r6/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf11
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf9
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf11
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf5
-rw-r--r--tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py207
-rw-r--r--tests/topotests/ospf6_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf30
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json95
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt1/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json183
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt2/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf41
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json144
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json28
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt3/zebra.conf24
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json188
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt4/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf29
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json100
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt5/zebra.conf20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf35
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json183
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json20
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt6/zebra.conf22
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf30
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json95
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json12
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json74
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json139
-rw-r--r--tests/topotests/ospf6_gr_topo1/rt7/zebra.conf22
-rwxr-xr-xtests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py458
-rw-r--r--tests/topotests/ospf6_topo1/README.md132
-rw-r--r--tests/topotests/ospf6_topo1/r1/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r1/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r1/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1/r1/zebra.conf20
-rw-r--r--tests/topotests/ospf6_topo1/r2/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r2/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r2/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r2/zebra.conf20
-rw-r--r--tests/topotests/ospf6_topo1/r3/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r3/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r3/ospf6d.conf37
-rw-r--r--tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r3/zebra.conf23
-rw-r--r--tests/topotests/ospf6_topo1/r4/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r4/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1/r4/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1/r4/zebra.conf20
-rw-r--r--tests/topotests/ospf6_topo1/test_ospf6_topo1.py435
-rw-r--r--tests/topotests/ospf6_topo1_vrf/README.md132
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.nhg.ref10
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r1/zebra.conf20
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r2/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r2/zebra.conf20
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r3/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf37
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r3/zebra.conf23
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r4/ip_6_address.ref10
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf31
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref9
-rw-r--r--tests/topotests/ospf6_topo1_vrf/r4/zebra.conf20
-rwxr-xr-xtests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py475
-rw-r--r--tests/topotests/ospf6_topo2/r1/ospf6d.conf37
-rw-r--r--tests/topotests/ospf6_topo2/r1/zebra.conf5
-rw-r--r--tests/topotests/ospf6_topo2/r2/ospf6d.conf51
-rw-r--r--tests/topotests/ospf6_topo2/r2/zebra.conf11
-rw-r--r--tests/topotests/ospf6_topo2/r3/ospf6d.conf38
-rw-r--r--tests/topotests/ospf6_topo2/r3/zebra.conf8
-rw-r--r--tests/topotests/ospf6_topo2/r4/ospf6d.conf37
-rw-r--r--tests/topotests/ospf6_topo2/r4/zebra.conf5
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.dot83
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.pngbin0 -> 44388 bytes
-rw-r--r--tests/topotests/ospf6_topo2/test_ospf6_topo2.py665
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json195
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json199
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_authentication.json166
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_chaos.json166
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_ecmp.json342
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json234
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_lan.json138
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_nssa.json188
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_p2mp.json198
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_routemaps.json159
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_rte_calc.json168
-rw-r--r--tests/topotests/ospf_basic_functionality/ospf_single_area.json188
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py3313
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py409
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_authentication.py873
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_chaos.py530
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py457
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py307
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_lan.py665
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_nssa.py279
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py537
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py1315
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py795
-rw-r--r--tests/topotests/ospf_basic_functionality/test_ospf_single_area.py1015
-rw-r--r--tests/topotests/ospf_dual_stack/test_ospf_dual_stack.dot107
-rw-r--r--tests/topotests/ospf_dual_stack/test_ospf_dual_stack.jpgbin0 -> 98314 bytes
-rw-r--r--tests/topotests/ospf_dual_stack/test_ospf_dual_stack.json263
-rw-r--r--tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py122
-rw-r--r--tests/topotests/ospf_gr_helper/ospf_gr_helper.json119
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py397
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py375
-rw-r--r--tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py327
-rw-r--r--tests/topotests/ospf_gr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/ospfd.conf32
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json98
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json180
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt1/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json160
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json201
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt2/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/ospfd.conf43
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json83
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json25
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json223
-rw-r--r--tests/topotests/ospf_gr_topo1/rt3/zebra.conf26
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/ospfd.conf37
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json164
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json202
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt4/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/ospfd.conf31
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json102
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json203
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json225
-rw-r--r--tests/topotests/ospf_gr_topo1/rt5/zebra.conf20
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/ospfd.conf38
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json18
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json214
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json224
-rw-r--r--tests/topotests/ospf_gr_topo1/rt6/zebra.conf23
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/ospfd.conf33
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json99
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json11
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json168
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json210
-rw-r--r--tests/topotests/ospf_gr_topo1/rt7/zebra.conf23
-rwxr-xr-xtests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py462
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/ospf_default_information.json33
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa.json17
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa2.json25
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/ospfd-3.conf2
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json13
-rw-r--r--tests/topotests/ospf_instance_redistribute/r1/zebra.conf2
-rw-r--r--tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py188
-rwxr-xr-xtests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py0
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf57
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt19
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt12
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt9
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt6
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf62
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt20
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt15
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt10
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt9
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf22
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt17
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt8
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf22
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt17
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt7
-rw-r--r--tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py243
-rwxr-xr-xtests/topotests/ospf_netns_vrf/__init__.py0
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/ospfd.conf13
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/ospfroute.txt18
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/ospfroute_down.txt14
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/zebra.conf17
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/zebraroute.txt8
-rw-r--r--tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt7
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/ospfd.conf14
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/ospfroute.txt18
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/ospfroute_down.txt14
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/zebra.conf13
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/zebraroute.txt8
-rw-r--r--tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt7
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/ospfd.conf15
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/ospfroute.txt18
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/ospfroute_down.txt8
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/zebra.conf13
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/zebraroute.txt8
-rw-r--r--tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt4
-rw-r--r--tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.dot78
-rw-r--r--tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.jpgbin0 -> 65859 bytes
-rw-r--r--tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py324
-rw-r--r--tests/topotests/ospf_sr_te_topo1/dst/zebra.conf23
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf16
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf35
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf27
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref20
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref20
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref25
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf21
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf46
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf35
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf45
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf33
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf52
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf43
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf52
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf43
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf12
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf40
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf25
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref13
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref19
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref19
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref23
-rw-r--r--tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf38
-rwxr-xr-xtests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py666
-rw-r--r--tests/topotests/ospf_sr_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/ospfd.conf31
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step1/show_ip_route.ref289
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step1/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step10/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step10/show_mpls_table.ref79
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step2/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step2/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step3/show_ip_route.ref249
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step3/show_mpls_table.ref50
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step4/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step4/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step5/show_ip_route.ref276
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step5/show_mpls_table.ref50
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step6/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step6/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step7/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step7/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step8/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step8/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step9/show_ip_route.ref282
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/step9/show_mpls_table.ref79
-rw-r--r--tests/topotests/ospf_sr_topo1/rt1/zebra.conf18
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/ospfd.conf41
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step1/show_ip_route.ref330
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step1/show_mpls_table.ref97
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step10/show_ip_route.ref254
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step10/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step2/show_ip_route.ref303
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step2/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step3/show_ip_route.ref256
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step3/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step4/show_ip_route.ref303
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step4/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step5/show_ip_route.ref297
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step5/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step6/show_ip_route.ref303
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step6/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step7/show_ip_route.ref300
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step7/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step8/show_ip_route.ref303
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step8/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step9/show_ip_route.ref303
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/step9/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt2/zebra.conf24
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/ospfd.conf41
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step1/show_ip_route.ref330
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step1/show_mpls_table.ref97
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step10/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step10/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step2/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step2/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step3/show_ip_route.ref263
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step3/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step4/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step4/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step5/show_ip_route.ref304
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step5/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step6/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step6/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step7/show_ip_route.ref307
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step7/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step8/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step8/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step9/show_ip_route.ref310
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/step9/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt3/zebra.conf24
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/ospfd.conf46
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step1/show_ip_route.ref311
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step1/show_mpls_table.ref97
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step10/show_ip_route.ref278
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step10/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step2/show_ip_route.ref324
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step2/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step3/show_ip_route.ref296
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step3/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step4/show_ip_route.ref324
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step4/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step5/show_ip_route.ref318
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step5/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step6/show_ip_route.ref324
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step6/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step7/show_ip_route.ref318
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step7/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step8/show_ip_route.ref324
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step8/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step9/show_ip_route.ref324
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/step9/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt4/zebra.conf27
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/ospfd.conf46
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step1/show_ip_route.ref311
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step1/show_mpls_table.ref97
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step10/show_ip_route.ref300
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step10/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step2/show_ip_route.ref307
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step2/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step3/show_ip_route.ref272
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step3/show_mpls_table.ref85
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step4/show_ip_route.ref307
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step4/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step5/show_ip_route.ref286
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step5/show_mpls_table.ref67
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step6/show_ip_route.ref307
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step6/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step7/show_ip_route.ref286
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step7/show_mpls_table.ref73
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step8/show_ip_route.ref307
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step8/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step9/show_ip_route.ref292
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/step9/show_mpls_table.ref91
-rw-r--r--tests/topotests/ospf_sr_topo1/rt5/zebra.conf27
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/ospfd.conf36
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step1/show_ip_route.ref291
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step1/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step10/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step10/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step2/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step2/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step3/show_ip_route.ref2
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step3/show_mpls_table.ref2
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step4/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step4/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step5/show_ip_route.ref266
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step5/show_mpls_table.ref2
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step6/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step6/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step7/show_ip_route.ref278
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step7/show_mpls_table.ref50
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step8/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step8/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step9/show_ip_route.ref284
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/step9/show_mpls_table.ref68
-rw-r--r--tests/topotests/ospf_sr_topo1/rt6/zebra.conf21
-rw-r--r--tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py682
-rwxr-xr-xtests/topotests/ospf_suppress_fa/__init__.py0
-rw-r--r--tests/topotests/ospf_suppress_fa/r1/ospfd.conf9
-rw-r--r--tests/topotests/ospf_suppress_fa/r1/zebra.conf4
-rw-r--r--tests/topotests/ospf_suppress_fa/r2/ospfd.conf16
-rw-r--r--tests/topotests/ospf_suppress_fa/r2/zebra.conf7
-rw-r--r--tests/topotests/ospf_suppress_fa/r3/ospfd.conf11
-rw-r--r--tests/topotests/ospf_suppress_fa/r3/zebra.conf8
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot66
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpgbin0 -> 33501 bytes
-rw-r--r--tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py188
-rwxr-xr-xtests/topotests/ospf_te_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_te_topo1/r1/ospfd.conf23
-rw-r--r--tests/topotests/ospf_te_topo1/r1/zebra.conf21
-rw-r--r--tests/topotests/ospf_te_topo1/r2/ospfd.conf34
-rw-r--r--tests/topotests/ospf_te_topo1/r2/zebra.conf33
-rw-r--r--tests/topotests/ospf_te_topo1/r3/ospfd.conf24
-rw-r--r--tests/topotests/ospf_te_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ospf_te_topo1/r4/ospfd.conf21
-rw-r--r--tests/topotests/ospf_te_topo1/r4/zebra.conf12
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step1.json571
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step2.json473
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step3.json405
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step4.json486
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step5.json608
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step6.json609
-rw-r--r--tests/topotests/ospf_te_topo1/reference/ted_step7.json451
-rw-r--r--tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py295
-rw-r--r--tests/topotests/ospf_tilfa_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf27
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/step1/show_ip_route_initial.ref156
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_initial.ref156
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_link_protection.ref226
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_initial.ref156
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_node_protection.ref192
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf17
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf27
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf17
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf27
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf17
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf27
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf17
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf27
-rw-r--r--tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf17
-rw-r--r--tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py239
-rwxr-xr-xtests/topotests/ospf_topo1/__init__.py0
-rw-r--r--tests/topotests/ospf_topo1/r1/ospf6d.conf13
-rw-r--r--tests/topotests/ospf_topo1/r1/ospf6route.txt13
-rw-r--r--tests/topotests/ospf_topo1/r1/ospf6route_down.txt5
-rw-r--r--tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt13
-rw-r--r--tests/topotests/ospf_topo1/r1/ospfd.conf13
-rw-r--r--tests/topotests/ospf_topo1/r1/ospfroute.txt23
-rw-r--r--tests/topotests/ospf_topo1/r1/ospfroute_down.txt13
-rw-r--r--tests/topotests/ospf_topo1/r1/zebra.conf11
-rw-r--r--tests/topotests/ospf_topo1/r2/ospf6d.conf17
-rw-r--r--tests/topotests/ospf_topo1/r2/ospf6route.txt13
-rw-r--r--tests/topotests/ospf_topo1/r2/ospf6route_down.txt5
-rw-r--r--tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt13
-rw-r--r--tests/topotests/ospf_topo1/r2/ospfd.conf13
-rw-r--r--tests/topotests/ospf_topo1/r2/ospfroute.txt23
-rw-r--r--tests/topotests/ospf_topo1/r2/ospfroute_down.txt13
-rw-r--r--tests/topotests/ospf_topo1/r2/zebra.conf11
-rw-r--r--tests/topotests/ospf_topo1/r3/ospf6d.conf22
-rw-r--r--tests/topotests/ospf_topo1/r3/ospf6route.txt12
-rw-r--r--tests/topotests/ospf_topo1/r3/ospf6route_down.txt5
-rw-r--r--tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt12
-rw-r--r--tests/topotests/ospf_topo1/r3/ospfd.conf18
-rw-r--r--tests/topotests/ospf_topo1/r3/ospfroute.txt23
-rw-r--r--tests/topotests/ospf_topo1/r3/ospfroute_down.txt13
-rw-r--r--tests/topotests/ospf_topo1/r3/zebra.conf15
-rw-r--r--tests/topotests/ospf_topo1/r4/ospf6d.conf17
-rw-r--r--tests/topotests/ospf_topo1/r4/ospf6route.txt13
-rw-r--r--tests/topotests/ospf_topo1/r4/ospf6route_down.txt5
-rw-r--r--tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt12
-rw-r--r--tests/topotests/ospf_topo1/r4/ospfd.conf13
-rw-r--r--tests/topotests/ospf_topo1/r4/ospfroute.txt23
-rw-r--r--tests/topotests/ospf_topo1/r4/ospfroute_down.txt13
-rw-r--r--tests/topotests/ospf_topo1/r4/zebra.conf11
-rw-r--r--tests/topotests/ospf_topo1/test_ospf_topo1.dot104
-rw-r--r--tests/topotests/ospf_topo1/test_ospf_topo1.jpgbin0 -> 123663 bytes
-rw-r--r--tests/topotests/ospf_topo1/test_ospf_topo1.py598
-rw-r--r--tests/topotests/ospf_unnumbered/r1/ospf-route.json1
-rw-r--r--tests/topotests/ospf_unnumbered/r1/ospfd.conf13
-rw-r--r--tests/topotests/ospf_unnumbered/r1/v4_route.json84
-rw-r--r--tests/topotests/ospf_unnumbered/r1/zebra.conf7
-rw-r--r--tests/topotests/ospf_unnumbered/r2/ospf-route.json1
-rw-r--r--tests/topotests/ospf_unnumbered/r2/ospfd.conf13
-rw-r--r--tests/topotests/ospf_unnumbered/r2/v4_route.json84
-rw-r--r--tests/topotests/ospf_unnumbered/r2/zebra.conf7
-rw-r--r--tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py165
-rwxr-xr-xtests/topotests/ospfapi/ctester.py171
l---------tests/topotests/ospfapi/lib1
-rw-r--r--tests/topotests/ospfapi/r1/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r1/zebra.conf6
-rw-r--r--tests/topotests/ospfapi/r2/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r2/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r3/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r3/zebra.conf7
-rw-r--r--tests/topotests/ospfapi/r4/ospfd.conf15
-rw-r--r--tests/topotests/ospfapi/r4/zebra.conf6
-rw-r--r--tests/topotests/ospfapi/test_ospf_clientapi.py1147
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json198
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json202
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json169
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json312
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json377
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json264
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json140
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_nssa.json86
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json197
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json180
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json190
-rw-r--r--tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json190
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py2753
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py1446
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py489
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py400
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py162
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py482
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py1248
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py923
-rw-r--r--tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py1342
-rw-r--r--tests/topotests/pbr_topo1/__init__.py0
-rw-r--r--tests/topotests/pbr_topo1/r1/linux-rules.json19
-rw-r--r--tests/topotests/pbr_topo1/r1/pbr-interface.json27
-rw-r--r--tests/topotests/pbr_topo1/r1/pbr-map.json152
-rw-r--r--tests/topotests/pbr_topo1/r1/pbr-nexthop-groups.json95
-rw-r--r--tests/topotests/pbr_topo1/r1/pbrd.conf100
-rw-r--r--tests/topotests/pbr_topo1/r1/zebra.conf14
-rw-r--r--tests/topotests/pbr_topo1/test_pbr_topo1.py288
-rw-r--r--tests/topotests/pim_acl/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_acl/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_acl/r1/acl_1_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_2_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_3_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_4_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_5_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/acl_6_pim_join.json21
-rw-r--r--tests/topotests/pim_acl/r1/ospf_neighbor.json59
-rw-r--r--tests/topotests/pim_acl/r1/ospfd.conf16
-rw-r--r--tests/topotests/pim_acl/r1/pim_neighbor.json31
-rw-r--r--tests/topotests/pim_acl/r1/pimd.conf31
-rw-r--r--tests/topotests/pim_acl/r1/zebra.conf18
-rw-r--r--tests/topotests/pim_acl/r11/acl_1_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r12/acl_2_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r12/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r13/acl_3_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r13/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r13/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r13/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r14/acl_4_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/acl_5_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r14/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r14/pimd.conf18
-rw-r--r--tests/topotests/pim_acl/r14/zebra.conf13
-rw-r--r--tests/topotests/pim_acl/r15/acl_6_pim_join.json19
-rw-r--r--tests/topotests/pim_acl/r15/ospfd.conf14
-rw-r--r--tests/topotests/pim_acl/r15/pimd.conf17
-rw-r--r--tests/topotests/pim_acl/r15/zebra.conf13
-rwxr-xr-xtests/topotests/pim_acl/test_pim_acl.py345
-rwxr-xr-xtests/topotests/pim_basic/mcast-rx.py92
-rwxr-xr-xtests/topotests/pim_basic/mcast-tx.py97
-rw-r--r--tests/topotests/pim_basic/r1/bgpd.conf5
-rw-r--r--tests/topotests/pim_basic/r1/pimd.conf18
-rw-r--r--tests/topotests/pim_basic/r1/rp-info.json9
-rw-r--r--tests/topotests/pim_basic/r1/zebra.conf14
-rw-r--r--tests/topotests/pim_basic/r2/pimd.conf1
-rw-r--r--tests/topotests/pim_basic/r2/zebra.conf8
-rw-r--r--tests/topotests/pim_basic/r3/pimd.conf1
-rw-r--r--tests/topotests/pim_basic/r3/zebra.conf8
-rw-r--r--tests/topotests/pim_basic/rp/bgpd.conf5
-rw-r--r--tests/topotests/pim_basic/rp/pimd.conf13
-rw-r--r--tests/topotests/pim_basic/rp/upstream.json17
-rw-r--r--tests/topotests/pim_basic/rp/zebra.conf8
-rw-r--r--tests/topotests/pim_basic/test_pim.py248
-rw-r--r--tests/topotests/pim_basic_topo2/__init__.py0
-rw-r--r--tests/topotests/pim_basic_topo2/r1/bfdd.conf6
-rw-r--r--tests/topotests/pim_basic_topo2/r1/pimd.conf4
-rw-r--r--tests/topotests/pim_basic_topo2/r1/zebra.conf3
-rw-r--r--tests/topotests/pim_basic_topo2/r2/bfdd.conf2
-rw-r--r--tests/topotests/pim_basic_topo2/r2/pimd.conf13
-rw-r--r--tests/topotests/pim_basic_topo2/r2/zebra.conf9
-rw-r--r--tests/topotests/pim_basic_topo2/r3/bfdd.conf2
-rw-r--r--tests/topotests/pim_basic_topo2/r3/pimd.conf4
-rw-r--r--tests/topotests/pim_basic_topo2/r3/zebra.conf3
-rw-r--r--tests/topotests/pim_basic_topo2/r4/bfdd.conf2
-rw-r--r--tests/topotests/pim_basic_topo2/r4/pimd.conf4
-rw-r--r--tests/topotests/pim_basic_topo2/r4/zebra.conf3
-rw-r--r--tests/topotests/pim_basic_topo2/test_pim_basic_topo2.dot73
-rw-r--r--tests/topotests/pim_basic_topo2/test_pim_basic_topo2.pngbin0 -> 33496 bytes
-rw-r--r--tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py238
-rw-r--r--tests/topotests/pim_igmp_vrf/h1/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h2/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/h3/zebra.conf10
-rw-r--r--tests/topotests/pim_igmp_vrf/h4/zebra.conf8
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json15
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json16
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/ospfd.conf26
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json22
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_join.json21
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json13
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json14
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/pimd.conf27
-rw-r--r--tests/topotests/pim_igmp_vrf/r1/zebra.conf30
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r11/zebra.conf13
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/ospfd.conf14
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pim_red_join.json19
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/pimd.conf17
-rw-r--r--tests/topotests/pim_igmp_vrf/r12/zebra.conf13
-rwxr-xr-xtests/topotests/pim_igmp_vrf/test_pim_vrf.py402
-rw-r--r--tests/topotests/pytest.ini83
-rw-r--r--tests/topotests/rip_topo1/r1/rip_status.ref22
-rw-r--r--tests/topotests/rip_topo1/r1/ripd.conf13
-rw-r--r--tests/topotests/rip_topo1/r1/show_ip_rip.ref12
-rw-r--r--tests/topotests/rip_topo1/r1/show_ip_route.ref3
-rw-r--r--tests/topotests/rip_topo1/r1/zebra.conf26
-rw-r--r--tests/topotests/rip_topo1/r2/rip_status.ref19
-rw-r--r--tests/topotests/rip_topo1/r2/ripd.conf12
-rw-r--r--tests/topotests/rip_topo1/r2/show_ip_rip.ref12
-rw-r--r--tests/topotests/rip_topo1/r2/show_ip_route.ref4
-rw-r--r--tests/topotests/rip_topo1/r2/zebra.conf21
-rw-r--r--tests/topotests/rip_topo1/r3/rip_status.ref16
-rw-r--r--tests/topotests/rip_topo1/r3/ripd.conf13
-rw-r--r--tests/topotests/rip_topo1/r3/show_ip_rip.ref12
-rw-r--r--tests/topotests/rip_topo1/r3/show_ip_route.ref3
-rw-r--r--tests/topotests/rip_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/rip_topo1/test_rip_topo1.dot61
-rw-r--r--tests/topotests/rip_topo1/test_rip_topo1.pdfbin0 -> 18433 bytes
-rw-r--r--tests/topotests/rip_topo1/test_rip_topo1.py364
-rw-r--r--tests/topotests/ripng_topo1/r1/ripng_status.ref20
-rw-r--r--tests/topotests/ripng_topo1/r1/ripngd.conf16
-rw-r--r--tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref18
-rw-r--r--tests/topotests/ripng_topo1/r1/show_ipv6_route.ref3
-rw-r--r--tests/topotests/ripng_topo1/r1/zebra.conf25
-rw-r--r--tests/topotests/ripng_topo1/r2/ripng_status.ref20
-rw-r--r--tests/topotests/ripng_topo1/r2/ripngd.conf13
-rw-r--r--tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref18
-rw-r--r--tests/topotests/ripng_topo1/r2/show_ipv6_route.ref4
-rw-r--r--tests/topotests/ripng_topo1/r2/zebra.conf21
-rw-r--r--tests/topotests/ripng_topo1/r3/ripng_status.ref16
-rw-r--r--tests/topotests/ripng_topo1/r3/ripngd.conf15
-rw-r--r--tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref18
-rw-r--r--tests/topotests/ripng_topo1/r3/show_ipv6_route.ref3
-rw-r--r--tests/topotests/ripng_topo1/r3/zebra.conf22
-rw-r--r--tests/topotests/ripng_topo1/test_ripng_topo1.dot59
-rw-r--r--tests/topotests/ripng_topo1/test_ripng_topo1.pdfbin0 -> 18609 bytes
-rw-r--r--tests/topotests/ripng_topo1/test_ripng_topo1.py406
-rw-r--r--tests/topotests/route_scale/r1/installed.routes.json16
-rw-r--r--tests/topotests/route_scale/r1/no.routes.json11
-rw-r--r--tests/topotests/route_scale/r1/sharpd.conf76
-rw-r--r--tests/topotests/route_scale/r1/zebra.conf96
-rw-r--r--tests/topotests/route_scale/scale_test_common.py215
-rw-r--r--tests/topotests/route_scale/test_route_scale1.py77
-rw-r--r--tests/topotests/route_scale/test_route_scale2.py77
-rw-r--r--tests/topotests/simple_snmp_test/r1/bgpd.conf6
-rw-r--r--tests/topotests/simple_snmp_test/r1/isisd.conf46
-rw-r--r--tests/topotests/simple_snmp_test/r1/snmpd.conf18
-rw-r--r--tests/topotests/simple_snmp_test/r1/zebra.conf22
-rwxr-xr-xtests/topotests/simple_snmp_test/test_simple_snmp.py131
-rw-r--r--tests/topotests/srv6_locator/__init__.py0
-rw-r--r--tests/topotests/srv6_locator/expected_chunks1.json1
-rw-r--r--tests/topotests/srv6_locator/expected_chunks2.json8
-rw-r--r--tests/topotests/srv6_locator/expected_chunks3.json1
-rw-r--r--tests/topotests/srv6_locator/expected_chunks4.json2
-rw-r--r--tests/topotests/srv6_locator/expected_chunks5.json2
-rw-r--r--tests/topotests/srv6_locator/expected_chunks6.json2
-rw-r--r--tests/topotests/srv6_locator/expected_ipv6_routes.json29
-rw-r--r--tests/topotests/srv6_locator/expected_locators1.json26
-rw-r--r--tests/topotests/srv6_locator/expected_locators2.json26
-rw-r--r--tests/topotests/srv6_locator/expected_locators3.json26
-rw-r--r--tests/topotests/srv6_locator/expected_locators4.json38
-rw-r--r--tests/topotests/srv6_locator/expected_locators5.json27
-rw-r--r--tests/topotests/srv6_locator/expected_locators6.json5
-rw-r--r--tests/topotests/srv6_locator/r1/setup.sh2
-rw-r--r--tests/topotests/srv6_locator/r1/sharpd.conf7
-rw-r--r--tests/topotests/srv6_locator/r1/zebra.conf22
-rwxr-xr-xtests/topotests/srv6_locator/test_srv6_locator.py164
-rw-r--r--tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json157
-rw-r--r--tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json363
-rw-r--r--tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json189
-rw-r--r--tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json428
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py1326
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py1760
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py1363
-rw-r--r--tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py989
-rw-r--r--tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json157
-rw-r--r--tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json371
-rw-r--r--tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json189
-rw-r--r--tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json428
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py1132
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py2033
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py891
-rw-r--r--tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py985
-rw-r--r--tests/topotests/subdir.am7
-rw-r--r--tests/topotests/zebra_multiple_connected/r1/ip_route.json62
-rw-r--r--tests/topotests/zebra_multiple_connected/r1/ip_route2.json102
-rw-r--r--tests/topotests/zebra_multiple_connected/r1/zebra.conf9
-rw-r--r--tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py162
-rw-r--r--tests/topotests/zebra_netlink/__init__.py0
-rw-r--r--tests/topotests/zebra_netlink/r1/zebra.conf2
-rw-r--r--tests/topotests/zebra_netlink/test_zebra_netlink.py120
-rw-r--r--tests/topotests/zebra_nht_resolution/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_nht_resolution/r1/zebra.conf5
-rw-r--r--tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py166
-rw-r--r--tests/topotests/zebra_opaque/__init__.py0
-rw-r--r--tests/topotests/zebra_opaque/r1/bgpd.conf7
-rw-r--r--tests/topotests/zebra_opaque/r1/zebra.conf4
-rw-r--r--tests/topotests/zebra_opaque/r2/bgpd.conf15
-rw-r--r--tests/topotests/zebra_opaque/r2/zebra.conf4
-rw-r--r--tests/topotests/zebra_opaque/r3/ospf6d.conf9
-rw-r--r--tests/topotests/zebra_opaque/r3/ospfd.conf9
-rw-r--r--tests/topotests/zebra_opaque/r3/zebra.conf5
-rw-r--r--tests/topotests/zebra_opaque/r4/ospf6d.conf9
-rw-r--r--tests/topotests/zebra_opaque/r4/ospfd.conf9
-rw-r--r--tests/topotests/zebra_opaque/r4/zebra.conf5
-rw-r--r--tests/topotests/zebra_opaque/test_zebra_opaque.py130
-rw-r--r--tests/topotests/zebra_rib/r1/iproute.ref512
-rw-r--r--tests/topotests/zebra_rib/r1/sharp_rmap.ref17
-rw-r--r--tests/topotests/zebra_rib/r1/static_rmap.ref9
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_1.json24
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_1_static_override.json40
-rw-r--r--tests/topotests/zebra_rib/r1/v4_route_2.json23
-rw-r--r--tests/topotests/zebra_rib/r1/zebra.conf26
-rw-r--r--tests/topotests/zebra_rib/test_zebra_rib.py340
-rw-r--r--tests/topotests/zebra_seg6_route/r1/routes.json25
-rw-r--r--tests/topotests/zebra_seg6_route/r1/setup.sh5
-rw-r--r--tests/topotests/zebra_seg6_route/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_seg6_route/r1/zebra.conf13
-rwxr-xr-xtests/topotests/zebra_seg6_route/test_zebra_seg6_route.py108
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/routes.json98
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/setup.sh3
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/sharpd.conf0
-rw-r--r--tests/topotests/zebra_seg6local_route/r1/zebra.conf9
-rwxr-xr-xtests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py113
3519 files changed, 397395 insertions, 0 deletions
diff --git a/tests/topotests/.gitignore b/tests/topotests/.gitignore
new file mode 100644
index 0000000..b1e3c30
--- /dev/null
+++ b/tests/topotests/.gitignore
@@ -0,0 +1,4 @@
+.cache
+__pycache__
+*.pyc
+.pytest_cache
diff --git a/tests/topotests/Dockerfile b/tests/topotests/Dockerfile
new file mode 100644
index 0000000..1503e67
--- /dev/null
+++ b/tests/topotests/Dockerfile
@@ -0,0 +1,87 @@
+FROM ubuntu:18.04
+
+RUN export DEBIAN_FRONTEND=noninteractive \
+ && apt-get update \
+ && apt-get install -y \
+ autoconf \
+ binutils \
+ bison \
+ ca-certificates \
+ flex \
+ gdb \
+ git \
+ gpg \
+ install-info \
+ iputils-ping \
+ iproute2 \
+ less \
+ libtool \
+ libjson-c-dev \
+ libpcre3-dev \
+ libpython-dev \
+ libpython3-dev \
+ libreadline-dev \
+ libc-ares-dev \
+ libcap-dev \
+ libelf-dev \
+ man \
+ mininet \
+ pkg-config \
+ python-pip \
+ python3 \
+ python3-dev \
+ python3-sphinx \
+ python3-pytest \
+ rsync \
+ strace \
+ tcpdump \
+ texinfo \
+ tmux \
+ valgrind \
+ vim \
+ wget \
+ x11-xserver-utils \
+ xterm \
+ && pip install \
+ exabgp==3.4.17 \
+ "scapy>=2.4.2" \
+ ipaddr \
+ pytest \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN export DEBIAN_FRONTEND=noninteractive \
+ && wget -qO- https://deb.frrouting.org/frr/keys.asc | apt-key add - \
+ && echo "deb https://deb.frrouting.org/frr bionic frr-stable" > /etc/apt/sources.list.d/frr.list \
+ && apt-get update \
+ && apt-get install -y libyang-dev \
+ && rm -rf /var/lib/apt/lists/*
+
+RUN groupadd -r -g 92 frr \
+ && groupadd -r -g 85 frrvty \
+ && useradd -c "FRRouting suite" \
+ -d /var/run/frr \
+ -g frr \
+ -G frrvty \
+ -r \
+ -s /sbin/nologin \
+ frr \
+ && useradd -d /var/run/exabgp/ \
+ -s /bin/false \
+ exabgp
+
+# Configure coredumps
+RUN echo "" >> /etc/security/limits.conf; \
+ echo "* soft core unlimited" >> /etc/security/limits.conf; \
+ echo "root soft core unlimited" >> /etc/security/limits.conf; \
+ echo "* hard core unlimited" >> /etc/security/limits.conf; \
+ echo "root hard core unlimited" >> /etc/security/limits.conf
+
+# Copy run scripts to facilitate users wanting to run the tests
+COPY docker/inner /opt/topotests
+
+ENV PATH "$PATH:/opt/topotests"
+
+RUN echo "cat /opt/topotests/motd.txt" >> /root/.profile && \
+ echo "export PS1='(topotests) $PS1'" >> /root/.profile
+
+ENTRYPOINT [ "bash", "/opt/topotests/entrypoint.sh" ]
diff --git a/tests/topotests/README.md b/tests/topotests/README.md
new file mode 100644
index 0000000..d9d849b
--- /dev/null
+++ b/tests/topotests/README.md
@@ -0,0 +1 @@
+Documentation is located in /doc/developer/topotests.rst
diff --git a/tests/topotests/all_protocol_startup/r1/babeld.conf b/tests/topotests/all_protocol_startup/r1/babeld.conf
new file mode 100644
index 0000000..3e119bf
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/babeld.conf
@@ -0,0 +1,4 @@
+router babel
+ network 192.168.1.1
+ network 192.168.2.1
+! \ No newline at end of file
diff --git a/tests/topotests/all_protocol_startup/r1/bgpd.conf b/tests/topotests/all_protocol_startup/r1/bgpd.conf
new file mode 100644
index 0000000..32dcb72
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/bgpd.conf
@@ -0,0 +1,60 @@
+log file bgpd.log
+!
+!
+router bgp 100
+ bgp router-id 192.168.0.1
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.7.10 remote-as 100
+ neighbor 192.168.7.10 timers 3 10
+ neighbor 192.168.7.20 remote-as 200
+ neighbor 192.168.7.20 timers 3 10
+ neighbor fc00:0:0:8::1000 remote-as 100
+ neighbor fc00:0:0:8::1000 timers 3 10
+ neighbor fc00:0:0:8::2000 remote-as 200
+ neighbor 192.168.7.10 description Transit_cogent
+ neighbor 192.168.7.20 description Client_Bibi_Full
+ neighbor fc00:0:0:8::1000 description Transit_cogent_v6
+ neighbor fc00:0:0:8::2000 description Client_Toto_default
+ neighbor fc00:0:0:8::2000 timers 3 10
+ !
+ address-family ipv4 unicast
+ network 192.168.0.0/24
+ neighbor 192.168.7.10 route-map bgp-map in
+ neighbor 192.168.7.10 filter-list bgp-filter-v4 out
+ neighbor 192.168.7.20 route-map bgp-map in
+ neighbor 192.168.7.20 filter-list bgp-filter-v4 out
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ network fc00::/64
+ neighbor fc00:0:0:8::1000 activate
+ neighbor fc00:0:0:8::1000 route-map bgp-map in
+ neighbor fc00:0:0:8::1000 filter-list bgp-filter-v6 out
+ neighbor fc00:0:0:8::2000 activate
+ neighbor fc00:0:0:8::2000 route-map bgp-map in
+ neighbor fc00:0:0:8::2000 filter-list bgp-filter-v6 out
+ exit-address-family
+!
+!
+ip prefix-list bgp-filter-v4 description dummy-test-prefix-list
+ip prefix-list bgp-filter-v4 seq 5 permit 192.168.0.0/24
+!
+ipv6 prefix-list bgp-filter-v4 seq 5 permit fc00::/64
+ipv6 prefix-list bgp-filter-v6 description dummy-test-prefix-list-v6
+!
+route-map bgp-map permit 10
+ set community 100:100 additive
+ set local-preference 100
+!
+route-map bgp-map permit 20
+ set metric 10
+ set local-preference 200
+!
+line vty
+!
+
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/r1/ip_nht.ref b/tests/topotests/all_protocol_startup/r1/ip_nht.ref
new file mode 100644
index 0000000..1da4da4
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ip_nht.ref
@@ -0,0 +1,72 @@
+1.1.1.1
+ resolved via static
+ is directly connected, r1-eth1
+ Client list: pbr(fd XX)
+1.1.1.2
+ resolved via static
+ is directly connected, r1-eth2
+ Client list: pbr(fd XX)
+1.1.1.3
+ resolved via static
+ is directly connected, r1-eth3
+ Client list: pbr(fd XX)
+1.1.1.4
+ resolved via static
+ is directly connected, r1-eth4
+ Client list: pbr(fd XX)
+1.1.1.5
+ resolved via static
+ is directly connected, r1-eth5
+ Client list: pbr(fd XX)
+1.1.1.6
+ resolved via static
+ is directly connected, r1-eth6
+ Client list: pbr(fd XX)
+1.1.1.7
+ resolved via static
+ is directly connected, r1-eth7
+ Client list: pbr(fd XX)
+1.1.1.8
+ resolved via static
+ is directly connected, r1-eth8
+ Client list: pbr(fd XX)
+2.2.2.1
+ unresolved
+ Client list: pbr(fd XX)
+4.4.4.1
+ unresolved
+ Client list: pbr(fd XX)
+4.4.4.2
+ unresolved
+ Client list: pbr(fd XX)
+6.6.6.1
+ unresolved
+ Client list: pbr(fd XX)
+6.6.6.2
+ unresolved
+ Client list: pbr(fd XX)
+6.6.6.3
+ unresolved
+ Client list: pbr(fd XX)
+6.6.6.4
+ unresolved
+ Client list: pbr(fd XX)
+192.168.0.2
+ resolved via connected
+ is directly connected, r1-eth0
+ Client list: static(fd XX)
+192.168.0.4
+ resolved via connected
+ is directly connected, r1-eth0
+ Client list: static(fd XX)
+192.168.7.10
+ resolved via connected
+ is directly connected, r1-eth7
+ Client list: bgp(fd XX)
+192.168.7.20(Connected)
+ resolved via connected
+ is directly connected, r1-eth7
+ Client list: bgp(fd XX)
+192.168.161.4
+ unresolved
+ Client list: pbr(fd XX)
diff --git a/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref
new file mode 100644
index 0000000..044cffa
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ipv4_routes.ref
@@ -0,0 +1,32 @@
+C>* 192.168.0.0/24 is directly connected, r1-eth0, XX:XX:XX
+C>* 192.168.1.0/26 is directly connected, r1-eth1, XX:XX:XX
+C>* 192.168.2.0/26 is directly connected, r1-eth2, XX:XX:XX
+C>* 192.168.3.0/26 is directly connected, r1-eth3, XX:XX:XX
+C>* 192.168.4.0/26 is directly connected, r1-eth4, XX:XX:XX
+C>* 192.168.5.0/26 is directly connected, r1-eth5, XX:XX:XX
+C>* 192.168.6.0/26 is directly connected, r1-eth6, XX:XX:XX
+C>* 192.168.7.0/26 is directly connected, r1-eth7, XX:XX:XX
+C>* 192.168.8.0/26 is directly connected, r1-eth8, XX:XX:XX
+C>* 192.168.9.0/26 is directly connected, r1-eth9, XX:XX:XX
+O 192.168.0.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX
+O 192.168.3.0/26 [110/10] is directly connected, r1-eth3, weight 1, XX:XX:XX
+S>* 1.1.1.1/32 [1/0] is directly connected, r1-eth1, weight 1, XX:XX:XX
+S>* 1.1.1.2/32 [1/0] is directly connected, r1-eth2, weight 1, XX:XX:XX
+S>* 1.1.1.3/32 [1/0] is directly connected, r1-eth3, weight 1, XX:XX:XX
+S>* 1.1.1.4/32 [1/0] is directly connected, r1-eth4, weight 1, XX:XX:XX
+S>* 1.1.1.5/32 [1/0] is directly connected, r1-eth5, weight 1, XX:XX:XX
+S>* 1.1.1.6/32 [1/0] is directly connected, r1-eth6, weight 1, XX:XX:XX
+S>* 1.1.1.7/32 [1/0] is directly connected, r1-eth7, weight 1, XX:XX:XX
+S>* 1.1.1.8/32 [1/0] is directly connected, r1-eth8, weight 1, XX:XX:XX
+S>* 4.5.6.10/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.11/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.12/32 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.13/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S>* 4.5.6.14/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S 4.5.6.15/32 [255/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX
+S 4.5.6.16/32 [10/0] via 192.168.0.4, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.16/32 [5/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.17/32 [1/0] via 192.168.0.2, r1-eth0, weight 1, XX:XX:XX
+S>* 4.5.6.7/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S>* 4.5.6.8/32 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S>* 4.5.6.9/32 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX
diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref b/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref
new file mode 100644
index 0000000..0255ecd
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ipv6_nht.ref
@@ -0,0 +1,13 @@
+fc00::2
+ resolved via connected
+ is directly connected, r1-eth0
+ Client list: static(fd XX)
+fc00:0:0:8::1000
+ resolved via connected
+ is directly connected, r1-eth8
+ Client list: bgp(fd XX)
+fc00:0:0:8::2000(Connected)
+ resolved via connected
+ is directly connected, r1-eth8
+ Client list: bgp(fd XX)
+ \ No newline at end of file
diff --git a/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref
new file mode 100644
index 0000000..ef12d61
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ipv6_routes.ref
@@ -0,0 +1,29 @@
+C>* fc00:0:0:1::/64 is directly connected, r1-eth1, XX:XX:XX
+C>* fc00:0:0:2::/64 is directly connected, r1-eth2, XX:XX:XX
+C>* fc00:0:0:3::/64 is directly connected, r1-eth3, XX:XX:XX
+C>* fc00:0:0:4::/64 is directly connected, r1-eth4, XX:XX:XX
+C>* fc00:0:0:5::/64 is directly connected, r1-eth5, XX:XX:XX
+C>* fc00:0:0:6::/64 is directly connected, r1-eth6, XX:XX:XX
+C>* fc00:0:0:7::/64 is directly connected, r1-eth7, XX:XX:XX
+C>* fc00:0:0:8::/64 is directly connected, r1-eth8, XX:XX:XX
+C>* fc00:0:0:9::/64 is directly connected, r1-eth9, XX:XX:XX
+C>* fc00::/64 is directly connected, r1-eth0, XX:XX:XX
+C>* fe80::/64 is directly connected, lo, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth0, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth1, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth2, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth3, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth4, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth5, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth6, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth7, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth8, XX:XX:XX
+C * fe80::/64 is directly connected, r1-eth9, XX:XX:XX
+O fc00:0:0:4::/64 [110/10] is directly connected, r1-eth4, weight 1, XX:XX:XX
+S>* 4:5::6:10/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX
+S>* 4:5::6:11/128 [1/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX
+S>* 4:5::6:12/128 [1/0] is directly connected, r1-eth0, weight 1, XX:XX:XX
+S 4:5::6:15/128 [255/0] via fc00::2, r1-eth0, weight 1, XX:XX:XX
+S>* 4:5::6:7/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S>* 4:5::6:8/128 [1/0] unreachable (blackhole), weight 1, XX:XX:XX
+S>* 4:5::6:9/128 [1/0] unreachable (ICMP unreachable), weight 1, XX:XX:XX
diff --git a/tests/topotests/all_protocol_startup/r1/isisd.conf b/tests/topotests/all_protocol_startup/r1/isisd.conf
new file mode 100644
index 0000000..8ceded8
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/isisd.conf
@@ -0,0 +1,21 @@
+log file isisd.log
+!
+! debug isis events
+!
+!
+interface r1-eth5
+ ip router isis test
+ isis circuit-type level-1
+!
+interface r1-eth6
+ ipv6 router isis test
+ isis circuit-type level-2-only
+!
+!
+router isis test
+ net 00.0001.00b0.64bc.43a0.00
+ metric-style wide
+ log-adjacency-changes
+!
+line vty
+!
diff --git a/tests/topotests/all_protocol_startup/r1/ldpd.conf b/tests/topotests/all_protocol_startup/r1/ldpd.conf
new file mode 100644
index 0000000..2358fb8
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ldpd.conf
@@ -0,0 +1,25 @@
+log file ldpd.log
+!
+! debug mpls ldp event
+! debug mpls ldp zebra
+!
+!
+mpls ldp
+ router-id 192.168.0.1
+ !
+ address-family ipv4
+ discovery transport-address 192.168.9.1
+ !
+ interface r1-eth9
+ !
+ !
+ address-family ipv6
+ discovery transport-address fc00:0:0:9::1
+ !
+ interface r1-eth9
+ !
+ !
+!
+!
+line vty
+!
diff --git a/tests/topotests/all_protocol_startup/r1/nhrpd.conf b/tests/topotests/all_protocol_startup/r1/nhrpd.conf
new file mode 100644
index 0000000..74e0f12
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/nhrpd.conf
@@ -0,0 +1 @@
+! \ No newline at end of file
diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf b/tests/topotests/all_protocol_startup/r1/ospf6d.conf
new file mode 100644
index 0000000..2e45186
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf
@@ -0,0 +1,19 @@
+log file ospf6d.log
+!
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+!
+interface r1-eth4
+!
+router ospf6
+ ospf6 router-id 192.168.0.1
+ log-adjacency-changes
+ interface r1-eth4 area 0.0.0.0
+!
+line vty
+!
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4 b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4
new file mode 100644
index 0000000..6d870f3
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ospf6d.conf-pre-v4
@@ -0,0 +1,16 @@
+log file ospf6d.log
+!
+debug ospf6 lsa unknown
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+!
+interface r1-eth4
+!
+router ospf6
+ router-id 192.168.0.1
+ log-adjacency-changes
+ interface r1-eth4 area 0.0.0.0
+!
+line vty
+!
diff --git a/tests/topotests/all_protocol_startup/r1/ospfd.conf b/tests/topotests/all_protocol_startup/r1/ospfd.conf
new file mode 100644
index 0000000..188f810
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ospfd.conf
@@ -0,0 +1,16 @@
+log file ospfd.log
+!
+! debug ospf event
+! debug ospf zebra
+!
+router ospf
+ ospf router-id 192.168.0.1
+ log-adjacency-changes
+ network 192.168.0.0/24 area 0.0.0.0
+ network 192.168.3.0/24 area 0.0.0.0
+!
+line vty
+!
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/r1/pbrd.conf b/tests/topotests/all_protocol_startup/r1/pbrd.conf
new file mode 100644
index 0000000..360fb13
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/pbrd.conf
@@ -0,0 +1,10 @@
+log file pbrd.log
+
+nexthop-group A
+ nexthop 192.168.161.4
+!
+pbr-map FOO seq 10
+ match dst-ip 4.5.6.7/32
+ match src-ip 6.7.8.8/32
+ set nexthop-group A
+! \ No newline at end of file
diff --git a/tests/topotests/all_protocol_startup/r1/rip_status.ref b/tests/topotests/all_protocol_startup/r1/rip_status.ref
new file mode 100644
index 0000000..4a5255f
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/rip_status.ref
@@ -0,0 +1,15 @@
+Routing Protocol is "rip"
+ Sending updates every 30 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 120 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 2, receive version 2
+ Interface Send Recv Key-chain
+ r1-eth1 2 2
+ Routing for Networks:
+ 192.168.1.0/26
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ Distance: (default is 120)
diff --git a/tests/topotests/all_protocol_startup/r1/ripd.conf b/tests/topotests/all_protocol_startup/r1/ripd.conf
new file mode 100644
index 0000000..0a06794
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ripd.conf
@@ -0,0 +1,15 @@
+log file ripd.log
+!
+! debug rip events
+! debug rip zebra
+!
+router rip
+ version 2
+ network 192.168.1.0/26
+!
+line vty
+!
+
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/r1/ripng_status.ref b/tests/topotests/all_protocol_startup/r1/ripng_status.ref
new file mode 100644
index 0000000..5d67c14
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ripng_status.ref
@@ -0,0 +1,14 @@
+Routing Protocol is "RIPng"
+ Sending updates every 30 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 120 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 1, receive version 1
+ Interface Send Recv
+ r1-eth2 1 1
+ Routing for Networks:
+ fc00:0:0:2::/64
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
diff --git a/tests/topotests/all_protocol_startup/r1/ripngd.conf b/tests/topotests/all_protocol_startup/r1/ripngd.conf
new file mode 100644
index 0000000..d9d900f
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/ripngd.conf
@@ -0,0 +1,14 @@
+log file ripngd.log
+!
+! debug ripng events
+! debug ripng zebra
+!
+router ripng
+ network fc00:0:0:2::/64
+!
+line vty
+!
+
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref
new file mode 100644
index 0000000..b38701a
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post4.1.ref
@@ -0,0 +1,9 @@
+BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes: i - IGP, e - EGP, ? - incomplete
+RPKI validation codes: V valid, I invalid, N Not found
+
+ Network Next Hop Metric LocPrf Weight Path
+*> 192.168.0.0 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref
new file mode 100644
index 0000000..82b64c0
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post5.0.ref
@@ -0,0 +1,9 @@
+BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes: i - IGP, e - EGP, ? - incomplete
+RPKI validation codes: V valid, I invalid, N Not found
+
+ Network Next Hop Metric LocPrf Weight Path
+*> 192.168.0.0/24 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref
new file mode 100644
index 0000000..fd333b3
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4-post6.1.ref
@@ -0,0 +1,10 @@
+BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
+Default local pref 100, local AS 100
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes: i - IGP, e - EGP, ? - incomplete
+RPKI validation codes: V valid, I invalid, N Not found
+
+ Network Next Hop Metric LocPrf Weight Path
+*> 192.168.0.0/24 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref
new file mode 100644
index 0000000..3be6cd3
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv4.ref
@@ -0,0 +1,7 @@
+BGP table version is 1, local router ID is 192.168.0.1
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Origin codes: i - IGP, e - EGP, ? - incomplete
+
+ Network Next Hop Metric LocPrf Weight Path
+*> 192.168.0.0 0.0.0.0 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref
new file mode 100644
index 0000000..20034b7
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6-post4.1.ref
@@ -0,0 +1,9 @@
+BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes: i - IGP, e - EGP, ? - incomplete
+RPKI validation codes: V valid, I invalid, N Not found
+
+ Network Next Hop Metric LocPrf Weight Path
+*> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref
new file mode 100644
index 0000000..fffee63
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6.ref
@@ -0,0 +1,7 @@
+BGP table version is 1, local router ID is 192.168.0.1
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Origin codes: i - IGP, e - EGP, ? - incomplete
+
+ Network Next Hop Metric LocPrf Weight Path
+*> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref
new file mode 100644
index 0000000..5b5f859
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_post6.1.ref
@@ -0,0 +1,10 @@
+BGP table version is 1, local router ID is 192.168.0.1, vrf id 0
+Default local pref 100, local AS 100
+Status codes: s suppressed, d damped, h history, * valid, > best, = multipath,
+ i internal, r RIB-failure, S Stale, R Removed
+Nexthop codes: @NNN nexthop's vrf id, < announce-nh-self
+Origin codes: i - IGP, e - EGP, ? - incomplete
+RPKI validation codes: V valid, I invalid, N Not found
+
+ Network Next Hop Metric LocPrf Weight Path
+*> fc00::/64 :: 0 32768 i
diff --git a/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref
new file mode 100644
index 0000000..0246687
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_bgp_ipv6_summary.ref
@@ -0,0 +1,8 @@
+BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
+BGP table version 1
+RIB entries 1, using XXXX bytes of memory
+Peers 2, using XXXX KiB of memory
+
+Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
+fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 Transit_cogent_v6
+fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 Client_Toto_default
diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref
new file mode 100644
index 0000000..deeae87
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_ip_bgp_summary.ref
@@ -0,0 +1,10 @@
+BGP router identifier 192.168.0.1, local AS number 100 vrf-id 0
+BGP table version 1
+RIB entries 1, using XXXX bytes of memory
+Peers 4, using XXXX KiB of memory
+
+Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd PfxSnt Desc
+192.168.7.10 4 100 0 0 0 0 0 never Active 0 Transit_cogent
+192.168.7.20 4 200 0 0 0 0 0 never Active 0 Client_Bibi_Full
+fc00:0:0:8::1000 4 100 0 0 0 0 0 never Active 0 Transit_cogent_v6
+fc00:0:0:8::2000 4 200 0 0 0 0 0 never Active 0 Client_Toto_default
diff --git a/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
new file mode 100644
index 0000000..ff85679
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_ip_ospf_interface.ref
@@ -0,0 +1,24 @@
+r1-eth0 is up
+ ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
+ Internet Address 192.168.0.1/24, Broadcast 192.168.0.255, Area 0.0.0.0
+ MTU mismatch detection: enabled
+ Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
+ Transmit Delay is 1 sec, State DR, Priority 1
+ Designated Router (ID) 192.168.0.1 Interface Address 192.168.0.1/24
+ No backup designated router on this network
+ Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters
+ Timer intervals configured, Hello 10s, Dead 40s, Wait 40s, Retransmit 5
+ Hello due in XX.XXXs
+ Neighbor Count is 0, Adjacent neighbor count is 0
+r1-eth3 is up
+ ifindex X, MTU 1500 bytes, BW XX Mbit <UP,BROADCAST,RUNNING,MULTICAST>
+ Internet Address 192.168.3.1/26, Broadcast 192.168.3.63, Area 0.0.0.0
+ MTU mismatch detection: enabled
+ Router ID 192.168.0.1, Network Type BROADCAST, Cost: 10
+ Transmit Delay is 1 sec, State DR, Priority 1
+ Designated Router (ID) 192.168.0.1 Interface Address 192.168.3.1/26
+ No backup designated router on this network
+ Multicast group memberships: OSPFAllRouters OSPFDesignatedRouters
+ Timer intervals configured, Hello 10s, Dead 40s, Wait 40s, Retransmit 5
+ Hello due in XX.XXXs
+ Neighbor Count is 0, Adjacent neighbor count is 0
diff --git a/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface
diff --git a/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref
new file mode 100644
index 0000000..6fbf40b
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_ipv6_ospf6_interface.ref
@@ -0,0 +1,46 @@
+lo is up, type LOOPBACK
+ Interface ID: 1
+ OSPF not enabled on this interface
+r1-eth0 is up, type BROADCAST
+ Interface ID: 2
+ OSPF not enabled on this interface
+r1-eth1 is up, type BROADCAST
+ Interface ID: 3
+ OSPF not enabled on this interface
+r1-eth2 is up, type BROADCAST
+ Interface ID: 4
+ OSPF not enabled on this interface
+r1-eth3 is up, type BROADCAST
+ Interface ID: 5
+ OSPF not enabled on this interface
+r1-eth4 is up, type BROADCAST
+ Interface ID: 6
+ Internet Address:
+ inet : 192.168.4.1/26
+ inet6: fc00:0:0:4::1/64
+ inet6: fe80::XXXX:XXXX:XXXX:XXXX/64
+ Instance ID 0, Interface MTU 1500 (autodetect: 1500)
+ MTU mismatch detection: enabled
+ Area ID 0.0.0.0, Cost 10
+ State DR, Transmit Delay 1 sec, Priority 1
+ Timer intervals configured:
+ Hello 10, Dead 40, Retransmit 5
+ DR: 192.168.0.1 BDR: 0.0.0.0
+ Number of I/F scoped LSAs is 1
+ 0 Pending LSAs for LSUpdate in Time 00:00:00 [thread off]
+ 0 Pending LSAs for LSAck in Time 00:00:00 [thread off]
+r1-eth5 is up, type BROADCAST
+ Interface ID: 7
+ OSPF not enabled on this interface
+r1-eth6 is up, type BROADCAST
+ Interface ID: 8
+ OSPF not enabled on this interface
+r1-eth7 is up, type BROADCAST
+ Interface ID: 9
+ OSPF not enabled on this interface
+r1-eth8 is up, type BROADCAST
+ Interface ID: 10
+ OSPF not enabled on this interface
+r1-eth9 is up, type BROADCAST
+ Interface ID: 11
+ OSPF not enabled on this interface
diff --git a/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref
new file mode 100644
index 0000000..0534b64
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_isis_interface_detail.ref
@@ -0,0 +1,28 @@
+Area test:
+ Interface: r1-eth5, State: Up, Active, Circuit Id: 0xXX
+ Type: lan, Level: L1, SNPA: XXXX.XXXX.XXXX
+ Level-1 Information:
+ Metric: 10, Active neighbors: 0
+ Hello interval: 3, Holddown count: 10 (pad)
+ CNSP interval: 10, PSNP interval: 2
+ LAN Priority: 64, is not DIS
+ IP Prefix(es):
+ 192.168.5.1/26
+ IPv6 Link-Locals:
+ fe80::XXXX:XXXX:XXXX:XXXX/64
+ IPv6 Prefixes:
+ fc00:0:0:5::1/64
+
+ Interface: r1-eth6, State: Up, Active, Circuit Id: 0xXX
+ Type: lan, Level: L2, SNPA: XXXX.XXXX.XXXX
+ Level-2 Information:
+ Metric: 10, Active neighbors: 0
+ Hello interval: 3, Holddown count: 10 (pad)
+ CNSP interval: 10, PSNP interval: 2
+ LAN Priority: 64, is not DIS
+ IP Prefix(es):
+ 192.168.6.1/26
+ IPv6 Link-Locals:
+ fe80::XXXX:XXXX:XXXX:XXXX/64
+ IPv6 Prefixes:
+ fc00:0:0:6::1/64
diff --git a/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref b/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref
new file mode 100644
index 0000000..c6bb01c
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_mpls_ldp_interface.ref
@@ -0,0 +1,3 @@
+AF Interface State Uptime Hello Timers ac
+ipv4 r1-eth9 ACTIVE xx:xx:xx 5/15 0
+ipv6 r1-eth9 ACTIVE xx:xx:xx 5/15 0
diff --git a/tests/topotests/all_protocol_startup/r1/show_route_map.ref b/tests/topotests/all_protocol_startup/r1/show_route_map.ref
new file mode 100644
index 0000000..612d0a7
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/show_route_map.ref
@@ -0,0 +1,72 @@
+ZEBRA:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+RIP:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+RIPNG:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+OSPF:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+OSPF6:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+BGP:
+route-map: LIES Invoked: 0 Optimization: enabled Processed Change: false
+ deny, sequence 10 Invoked 0
+ Match clauses:
+ interface notpresent
+ Set clauses:
+ Call clause:
+ Action:
+ Exit routemap
+route-map: bgp-map Invoked: 0 Optimization: enabled Processed Change: false
+ permit, sequence 10 Invoked 0
+ Match clauses:
+ Set clauses:
+ community 100:100 additive
+ local-preference 100
+ Call clause:
+ Action:
+ Exit routemap
+ permit, sequence 20 Invoked 0
+ Match clauses:
+ Set clauses:
+ metric 10
+ local-preference 200
+ Call clause:
+ Action:
+ Exit routemap
+ISIS:
diff --git a/tests/topotests/all_protocol_startup/r1/zebra.conf b/tests/topotests/all_protocol_startup/r1/zebra.conf
new file mode 100644
index 0000000..c5ef796
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/r1/zebra.conf
@@ -0,0 +1,120 @@
+log file zebra.log
+!
+hostname r1
+!
+# Create the various blackhole route types
+ip route 4.5.6.7/32 blackhole
+ipv6 route 4:5::6:7/128 blackhole
+ip route 4.5.6.8/32 Null0
+ipv6 route 4:5::6:8/128 Null0
+ip route 4.5.6.9/32 reject
+ipv6 route 4:5::6:9/128 reject
+# Test various spellings of NULL0 to make sure we accept them
+ip route 4.5.6.13/32 null0
+ip route 4.5.6.14/32 NULL0
+# Create normal gateway routes
+ip route 4.5.6.10/32 192.168.0.2
+ipv6 route 4:5::6:10/128 fc00:0:0:0::2
+# Create normal gateway + interface routes
+ip route 4.5.6.11/32 192.168.0.2 r1-eth0
+ipv6 route 4:5::6:11/128 fc00:0:0:0::2 r1-eth0
+# Create ifname routes
+ip route 4.5.6.12/32 r1-eth0
+ipv6 route 4:5::6:12/128 r1-eth0
+# Create a route that has a large admin distance
+# an admin distance of 255 should be accepted
+# by zebra but not installed.
+ip route 4.5.6.15/32 192.168.0.2 255
+ipv6 route 4:5::6:15/128 fc00:0:0:0::2 255
+# Routes to put into a nexthop-group
+ip route 1.1.1.1/32 r1-eth1
+ip route 1.1.1.2/32 r1-eth2
+ip route 1.1.1.3/32 r1-eth3
+ip route 1.1.1.4/32 r1-eth4
+ip route 1.1.1.5/32 r1-eth5
+ip route 1.1.1.6/32 r1-eth6
+ip route 1.1.1.7/32 r1-eth7
+ip route 1.1.1.8/32 r1-eth8
+
+# Create a route that has overlapping distance
+# so we have backups
+ip route 4.5.6.16/32 192.168.0.2 5
+ip route 4.5.6.16/32 192.168.0.4 10
+
+# Create routes that have different tags
+# and how we handle it
+ip route 4.5.6.17/32 192.168.0.2 tag 9000
+ip route 4.5.6.17/32 192.168.0.2 tag 10000
+
+!
+interface r1-eth0
+ description to sw0 - no routing protocol
+ ip address 192.168.0.1/24
+ ipv6 address fc00:0:0:0::1/64
+!
+interface r1-eth1
+ description to sw1 - RIP interface
+ ip address 192.168.1.1/26
+ ipv6 address fc00:0:0:1::1/64
+ no link-detect
+!
+interface r1-eth2
+ description to sw2 - RIPng interface
+ ip address 192.168.2.1/26
+ ipv6 address fc00:0:0:2::1/64
+ no link-detect
+!
+interface r1-eth3
+ description to sw3 - OSPFv2 interface
+ ip address 192.168.3.1/26
+ ipv6 address fc00:0:0:3::1/64
+ no link-detect
+!
+interface r1-eth4
+ description to sw4 - OSPFv3 interface
+ ip address 192.168.4.1/26
+ ipv6 address fc00:0:0:4::1/64
+ no link-detect
+!
+interface r1-eth5
+ description to sw5 - ISIS IPv4 interface
+ ip address 192.168.5.1/26
+ ipv6 address fc00:0:0:5::1/64
+ no link-detect
+!
+interface r1-eth6
+ description to sw6 - ISIS IPv6 interface
+ ip address 192.168.6.1/26
+ ipv6 address fc00:0:0:6::1/64
+ no link-detect
+!
+interface r1-eth7
+ description to sw7 - BGP IPv4 interface
+ ip address 192.168.7.1/26
+ ipv6 address fc00:0:0:7::1/64
+ no link-detect
+!
+interface r1-eth8
+ description to sw8 - BGP IPv6 interface
+ ip address 192.168.8.1/26
+ ipv6 address fc00:0:0:8::1/64
+ no link-detect
+!
+interface r1-eth9
+ description to sw9 - LDP interface
+ ip address 192.168.9.1/26
+ ipv6 address fc00:0:0:9::1/64
+
+ no link-detect
+!
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
+route-map LIES deny 10
+ match interface notpresent
+!
diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot b/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot
new file mode 100644
index 0000000..f39f8f8
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.dot
@@ -0,0 +1,61 @@
+## GraphViz file for test_all_protocol_startup
+##
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## No protocol: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #33ff99 light green
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+## LDP IPv4 #fedbe2 light pink
+##### Colors (see http://www.color-hex.com/)
+
+graph test_all_protocol_startup {
+
+ // title
+ labelloc="t";
+ label="Test Topologoy All Protocols Startup";
+
+ ######################
+ # Routers
+ ######################
+
+ # Main FRR Router with all protocols
+ R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled];
+
+ ######################
+ # Network Lists
+ ######################
+
+ SW0_STUB [label="SW0 (no protocol)\n192.168.1.0/24\nfc00:0:0:0::/64", fillcolor="#d0e0d0", style=filled];
+
+ SW1_RIP [label="SW1 RIP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#19e3d9", style=filled];
+ SW2_RIPNG [label="SW2 RIPng\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#fcb314", style=filled];
+ SW3_OSPF [label="SW3 OSPFv2\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#32b835", style=filled];
+ SW4_OSPFV3 [label="SW4 OSPFv3\n192.168.4.0/24\nfc00:0:0:4::/64", fillcolor="#19e3d9", style=filled];
+ SW5_ISIS_V4 [label="SW5 ISIS IPv4\n192.168.5.0/24\nfc00:0:0:5::/64", fillcolor="#33ff99", style=filled];
+ SW6_ISIS_V6 [label="SW6 ISIS IPv6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#9a81ec", style=filled];
+ SW7_BGP_V4 [label="SW7 BGP IPv4\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#eee3d3", style=filled];
+ SW8_BGP_V6 [label="SW8 BGP IPv6\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#fdff00", style=filled];
+ SW9_LDP [label="SW9 LDP\n192.168.9.0/24\nfc00:0:0:9::/64", fillcolor="#fedbe2", style=filled];
+
+ ######################
+ # Network Connections
+ ######################
+ R1 -- SW0_STUB [label = "eth0\n.1\n::1"];
+ R1 -- SW1_RIP [label = "eth1\n.1\n::1"];
+ R1 -- SW2_RIPNG [label = "eth2\n.1\n::1"];
+ R1 -- SW3_OSPF [label = "eth3\n.1\n::1"];
+ R1 -- SW4_OSPFV3 [label = "eth4\n.1\n::1"];
+ R1 -- SW5_ISIS_V4 [label = "eth5\n.1\n::1"];
+ R1 -- SW6_ISIS_V6 [label = "eth6\n.1\n::1"];
+ R1 -- SW7_BGP_V4 [label = "eth7\n.1\n::1"];
+ R1 -- SW8_BGP_V6 [label = "eth8\n.1\n::1"];
+ R1 -- SW9_LDP [label = "eth9\n.1\n::1"];
+
+}
diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf b/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf
new file mode 100644
index 0000000..23f69bc
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.pdf
Binary files differ
diff --git a/tests/topotests/all_protocol_startup/test_all_protocol_startup.py b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
new file mode 100644
index 0000000..ca8c005
--- /dev/null
+++ b/tests/topotests/all_protocol_startup/test_all_protocol_startup.py
@@ -0,0 +1,1684 @@
+#!/usr/bin/env python
+
+#
+# test_all_protocol_startup.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_all_protocol_startup.py: Test of all protocols at same time
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import glob
+from time import sleep
+
+pytestmark = [
+ pytest.mark.babeld,
+ pytest.mark.bgpd,
+ pytest.mark.isisd,
+ pytest.mark.nhrpd,
+ pytest.mark.ospfd,
+ pytest.mark.pbrd,
+ pytest.mark.ripd,
+]
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+fatal_error = ""
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ router = tgen.add_router("r1")
+ for i in range(0, 10):
+ tgen.add_switch("sw%d" % i).add_link(router)
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ global fatal_error
+
+ print("\n\n** %s: Setup Topology" % module.__name__)
+ print("******************************************\n")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ net = tgen.net
+
+ if net["r1"].get_routertype() != "frr":
+ fatal_error = "Test is only implemented for FRR"
+ sys.stderr.write("\n\nTest is only implemented for FRR - Skipping\n\n")
+ pytest.skip(fatal_error)
+
+ # Starting Routers
+ #
+ # Main router
+ for i in range(1, 2):
+ net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir, i))
+ if net["r1"].checkRouterVersion("<", "4.0"):
+ net["r%s" % i].loadConf(
+ "ospf6d", "%s/r%s/ospf6d.conf-pre-v4" % (thisDir, i)
+ )
+ else:
+ net["r%s" % i].loadConf("ospf6d", "%s/r%s/ospf6d.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("isisd", "%s/r%s/isisd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("bgpd", "%s/r%s/bgpd.conf" % (thisDir, i))
+ if net["r%s" % i].daemon_available("ldpd"):
+ # Only test LDPd if it's installed and Kernel >= 4.5
+ net["r%s" % i].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("sharpd")
+ net["r%s" % i].loadConf("nhrpd", "%s/r%s/nhrpd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("babeld", "%s/r%s/babeld.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("pbrd", "%s/r%s/pbrd.conf" % (thisDir, i))
+ tgen.gears["r%s" % i].start()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(module):
+ print("\n\n** %s: Shutdown Topology" % module.__name__)
+ print("******************************************\n")
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_router_running():
+ global fatal_error
+ tgen = get_topogen()
+ net = tgen.net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check if FRR is running on each Router node")
+ print("******************************************\n")
+ sleep(5)
+
+ # Starting Routers
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def test_error_messages_vtysh():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check for error messages on VTYSH")
+ print("******************************************\n")
+
+ failures = 0
+ for i in range(1, 2):
+ #
+ # First checking Standard Output
+ #
+
+ # VTYSH output from router
+ vtystdout = net["r%s" % i].cmd('vtysh -c "show version" 2> /dev/null').rstrip()
+
+ # Fix newlines (make them all the same)
+ vtystdout = ("\n".join(vtystdout.splitlines()) + "\n").rstrip()
+ # Drop everything starting with "FRRouting X.xx" message
+ vtystdout = re.sub(r"FRRouting [0-9]+.*", "", vtystdout, flags=re.DOTALL)
+
+ if vtystdout == "":
+ print("r%s StdOut ok" % i)
+
+ assert vtystdout == "", "Vtysh StdOut Output check failed for router r%s" % i
+
+ #
+ # Second checking Standard Error
+ #
+
+ # VTYSH StdErr output from router
+ vtystderr = net["r%s" % i].cmd('vtysh -c "show version" > /dev/null').rstrip()
+
+ # Fix newlines (make them all the same)
+ vtystderr = ("\n".join(vtystderr.splitlines()) + "\n").rstrip()
+ # # Drop everything starting with "FRRouting X.xx" message
+ # vtystderr = re.sub(r"FRRouting [0-9]+.*", "", vtystderr, flags=re.DOTALL)
+
+ if vtystderr == "":
+ print("r%s StdErr ok" % i)
+
+ assert vtystderr == "", "Vtysh StdErr Output check failed for router r%s" % i
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_error_messages_daemons():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check for error messages in daemons")
+ print("******************************************\n")
+
+ error_logs = ""
+
+ for i in range(1, 2):
+ log = net["r%s" % i].getStdErr("ripd")
+ if log:
+ error_logs += "r%s RIPd StdErr Output:\n" % i
+ error_logs += log
+ log = net["r%s" % i].getStdErr("ripngd")
+ if log:
+ error_logs += "r%s RIPngd StdErr Output:\n" % i
+ error_logs += log
+ log = net["r%s" % i].getStdErr("ospfd")
+ if log:
+ error_logs += "r%s OSPFd StdErr Output:\n" % i
+ error_logs += log
+ log = net["r%s" % i].getStdErr("ospf6d")
+ if log:
+ error_logs += "r%s OSPF6d StdErr Output:\n" % i
+ error_logs += log
+ log = net["r%s" % i].getStdErr("isisd")
+ # ISIS shows debugging enabled status on StdErr
+ # Remove these messages
+ log = re.sub(r"^IS-IS .* debugging is on.*", "", log).rstrip()
+ if log:
+ error_logs += "r%s ISISd StdErr Output:\n" % i
+ error_logs += log
+ log = net["r%s" % i].getStdErr("bgpd")
+ if log:
+ error_logs += "r%s BGPd StdErr Output:\n" % i
+ error_logs += log
+ if net["r%s" % i].daemon_available("ldpd"):
+ log = net["r%s" % i].getStdErr("ldpd")
+ if log:
+ error_logs += "r%s LDPd StdErr Output:\n" % i
+ error_logs += log
+
+ log = net["r1"].getStdErr("nhrpd")
+ # NHRPD shows YANG model not embedded messages
+ # Ignore these
+ log = re.sub(r".*YANG model.*not embedded.*", "", log).rstrip()
+ if log:
+ error_logs += "r%s NHRPd StdErr Output:\n" % i
+ error_logs += log
+
+ log = net["r1"].getStdErr("babeld")
+ if log:
+ error_logs += "r%s BABELd StdErr Output:\n" % i
+ error_logs += log
+
+ log = net["r1"].getStdErr("pbrd")
+ if log:
+ error_logs += "r%s PBRd StdErr Output:\n" % i
+ error_logs += log
+
+ log = net["r%s" % i].getStdErr("zebra")
+ if log:
+ error_logs += "r%s Zebra StdErr Output:\n" % i
+ error_logs += log
+
+ if error_logs:
+ sys.stderr.write(
+ "Failed check for StdErr Output on daemons:\n%s\n" % error_logs
+ )
+
+ # Ignoring the issue if told to ignore (ie not yet fixed)
+ if error_logs != "":
+ if os.environ.get("bamboo_TOPOTESTS_ISSUE_349") == "IGNORE":
+ sys.stderr.write(
+ "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349\n"
+ )
+ pytest.skip(
+ "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/349"
+ )
+
+ assert error_logs == "", "Daemons report errors to StdErr"
+
+
+def test_converge_protocols():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Waiting for protocols convergence")
+ print("******************************************\n")
+
+ # Not really implemented yet - just sleep 60 secs for now
+ sleep(60)
+
+ # Make sure that all daemons are running
+ failures = 0
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+ print("Show that v4 routes are right\n")
+ v4_routesFile = "%s/r%s/ipv4_routes.ref" % (thisDir, i)
+ expected = (
+ net["r%s" % i].cmd("sort {} 2> /dev/null".format(v4_routesFile)).rstrip()
+ )
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ actual = (
+ net["r%s" % i]
+ .cmd(
+ "vtysh -c \"show ip route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
+ )
+ .rstrip()
+ )
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual IP Routing Table",
+ title2="Expected IP RoutingTable",
+ )
+ if diff:
+ sys.stderr.write("r%s failed IP Routing table check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IP Routing table failed for r%s\n%s" % (i, diff)
+
+ failures = 0
+
+ print("Show that v6 routes are right\n")
+ v6_routesFile = "%s/r%s/ipv6_routes.ref" % (thisDir, i)
+ expected = (
+ net["r%s" % i].cmd("sort {} 2> /dev/null".format(v6_routesFile)).rstrip()
+ )
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ actual = (
+ net["r%s" % i]
+ .cmd(
+ "vtysh -c \"show ipv6 route\" | sed -e '/^Codes: /,/^\\s*$/d' | sort 2> /dev/null"
+ )
+ .rstrip()
+ )
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual IPv6 Routing Table",
+ title2="Expected IPv6 RoutingTable",
+ )
+ if diff:
+ sys.stderr.write("r%s failed IPv6 Routing table check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IPv6 Routing table failed for r%s\n%s" % (i, diff)
+
+
+def route_get_nhg_id(route_str):
+ net = get_topogen().net
+ output = net["r1"].cmd('vtysh -c "show ip route %s nexthop-group"' % route_str)
+ match = re.search(r"Nexthop Group ID: (\d+)", output)
+ assert match is not None, (
+ "Nexthop Group ID not found for sharpd route %s" % route_str
+ )
+
+ nhg_id = int(match.group(1))
+ return nhg_id
+
+
+def verify_nexthop_group(nhg_id, recursive=False, ecmp=0):
+ net = get_topogen().net
+ count = 0
+ valid = None
+ ecmpcount = None
+ depends = None
+ resolved_id = None
+ installed = None
+ found = False
+
+ while not found and count < 10:
+ count += 1
+ # Verify NHG is valid/installed
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
+ valid = re.search(r"Valid", output)
+ if valid is None:
+ found = False
+ sleep(1)
+ continue
+
+ if ecmp or recursive:
+ ecmpcount = re.search(r"Depends:.*\n", output)
+ if ecmpcount is None:
+ found = False
+ sleep(1)
+ continue
+
+ # list of IDs in group
+ depends = re.findall(r"\((\d+)\)", ecmpcount.group(0))
+
+ if ecmp:
+ if len(depends) != ecmp:
+ found = False
+ sleep(1)
+ continue
+ else:
+ # If recursive, we need to look at its resolved group
+ if len(depends) != 1:
+ found = False
+ sleep(1)
+ continue
+
+ resolved_id = int(depends[0])
+ verify_nexthop_group(resolved_id, False)
+ else:
+ installed = re.search(r"Installed", output)
+ if installed is None:
+ found = False
+ sleep(1)
+ continue
+ found = True
+
+ assert valid is not None, "Nexthop Group ID=%d not marked Valid" % nhg_id
+ if ecmp or recursive:
+ assert ecmpcount is not None, "Nexthop Group ID=%d has no depends" % nhg_id
+ if ecmp:
+ assert len(depends) == ecmp, (
+ "Nexthop Group ID=%d doesn't match ecmp size" % nhg_id
+ )
+ else:
+ assert len(depends) == 1, (
+ "Nexthop Group ID=%d should only have one recursive depend" % nhg_id
+ )
+ else:
+ assert installed is not None, (
+ "Nexthop Group ID=%d not marked Installed" % nhg_id
+ )
+
+
+def verify_route_nexthop_group(route_str, recursive=False, ecmp=0):
+ # Verify route and that zebra created NHGs for and they are valid/installed
+ nhg_id = route_get_nhg_id(route_str)
+ verify_nexthop_group(nhg_id, recursive, ecmp)
+
+
+def test_nexthop_groups():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Verifying Nexthop Groups")
+ print("******************************************\n")
+
+ ### Nexthop Group Tests
+
+ ## Basic test
+
+ # Create a lib nexthop-group
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group basic" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
+ )
+
+ # Create with sharpd using nexthop-group
+ net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.1 nexthop-group basic 1"')
+ verify_route_nexthop_group("2.2.2.1/32")
+
+ ## Connected
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group connected" -c "nexthop r1-eth1" -c "nexthop r1-eth2"'
+ )
+
+ net["r1"].cmd('vtysh -c "sharp install routes 2.2.2.2 nexthop-group connected 1"')
+ verify_route_nexthop_group("2.2.2.2/32")
+
+ ## Recursive
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group basic-recursive" -c "nexthop 2.2.2.1"'
+ )
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 3.3.3.1 nexthop-group basic-recursive 1"'
+ )
+
+ verify_route_nexthop_group("3.3.3.1/32", True)
+
+ ## Duplicate
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group duplicate" -c "nexthop 2.2.2.1" -c "nexthop 1.1.1.1"'
+ )
+
+ net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.2 nexthop-group duplicate 1"')
+
+ verify_route_nexthop_group("3.3.3.2/32")
+
+ ## Two 4-Way ECMP
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group fourA" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2" \
+ -c "nexthop 1.1.1.3" -c "nexthop 1.1.1.4"'
+ )
+
+ net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.1 nexthop-group fourA 1"')
+
+ verify_route_nexthop_group("4.4.4.1/32")
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group fourB" -c "nexthop 1.1.1.5" -c "nexthop 1.1.1.6" \
+ -c "nexthop 1.1.1.7" -c "nexthop 1.1.1.8"'
+ )
+
+ net["r1"].cmd('vtysh -c "sharp install routes 4.4.4.2 nexthop-group fourB 1"')
+
+ verify_route_nexthop_group("4.4.4.2/32")
+
+ ## Recursive to 8-Way ECMP
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group eight-recursive" -c "nexthop 4.4.4.1" -c "nexthop 4.4.4.2"'
+ )
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 5.5.5.1 nexthop-group eight-recursive 1"'
+ )
+
+ verify_route_nexthop_group("5.5.5.1/32")
+
+ ## 4-way ECMP Routes Pointing to Each Other
+
+ # This is to check for a bug with NH resolution where
+ # routes would infintely resolve to each other blowing
+ # up the resolved-> nexthop pointer.
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group infinite-recursive" -c "nexthop 6.6.6.1" -c "nexthop 6.6.6.2" \
+ -c "nexthop 6.6.6.3" -c "nexthop 6.6.6.4"'
+ )
+
+ # static route nexthops can recurse to
+
+ net["r1"].cmd('vtysh -c "c t" -c "ip route 6.6.6.0/24 1.1.1.1"')
+
+ # Make routes that point to themselves in ecmp
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.4 nexthop-group infinite-recursive 1"'
+ )
+ sleep(5)
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.3 nexthop-group infinite-recursive 1"'
+ )
+ sleep(5)
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.2 nexthop-group infinite-recursive 1"'
+ )
+ sleep(5)
+
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes 6.6.6.1 nexthop-group infinite-recursive 1"'
+ )
+
+ # Get routes and test if has too many (duplicate) nexthops
+ count = 0
+ dups = []
+ nhg_id = route_get_nhg_id("6.6.6.1/32")
+ while (len(dups) != 3) and count < 10:
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
+
+ dups = re.findall(r"(via 1\.1\.1\.1)", output)
+ if len(dups) != 3:
+ count += 1
+ sleep(1)
+
+ # Should find 3, itself is inactive
+ assert len(dups) == 3, (
+ "Route 6.6.6.1/32 with Nexthop Group ID=%d has wrong number of resolved nexthops"
+ % nhg_id
+ )
+
+ ## Remove all NHG routes
+
+ net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.1 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 2.2.2.2 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.1 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 3.3.3.2 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.1 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 4.4.4.2 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 5.5.5.1 1"')
+ net["r1"].cmd('vtysh -c "sharp remove routes 6.6.6.1 4"')
+ net["r1"].cmd('vtysh -c "c t" -c "no ip route 6.6.6.0/24 1.1.1.1"')
+
+
+def test_rip_status():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying RIP status")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/rip_status.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ip rip status" 2> /dev/null')
+ .rstrip()
+ )
+ # Drop time in next due
+ actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual IP RIP status",
+ title2="expected IP RIP status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_ripng_status():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying RIPng status")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Link-Local mac address portion. They are random...
+ actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
+ # Drop time in next due
+ actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual IPv6 RIPng status",
+ title2="expected IPv6 RIPng status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_ospfv2_interfaces():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying OSPFv2 interfaces")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/show_ip_ospf_interface.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ip ospf interface" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Bandwidth portion. They may change..
+ actual = re.sub(r"BW [0-9]+ Mbit", "BW XX Mbit", actual)
+ actual = re.sub(r"ifindex [0-9]+", "ifindex X", actual)
+
+ # Drop time in next due
+ actual = re.sub(r"Hello due in [0-9\.]+s", "Hello due in XX.XXXs", actual)
+ actual = re.sub(
+ r"Hello due in [0-9\.]+ usecs", "Hello due in XX.XXXs", actual
+ )
+ # Fix 'MTU mismatch detection: enabled' vs 'MTU mismatch detection:enabled' - accept both
+ actual = re.sub(
+ r"MTU mismatch detection:([a-z]+.*)",
+ r"MTU mismatch detection: \1",
+ actual,
+ )
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW IP OSPF INTERFACE",
+ title2="expected SHOW IP OSPF INTERFACE",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed SHOW IP OSPF INTERFACE check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ # Ignoring the issue if told to ignore (ie not yet fixed)
+ if failures != 0:
+ if os.environ.get("bamboo_TOPOTESTS_ISSUE_348") == "IGNORE":
+ sys.stderr.write(
+ "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348\n"
+ )
+ pytest.skip(
+ "Known issue - IGNORING. See https://github.com/FRRouting/frr/issues/348"
+ )
+
+ assert (
+ failures == 0
+ ), "SHOW IP OSPF INTERFACE failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_isis_interfaces():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying ISIS interfaces")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/show_isis_interface_detail.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show isis interface detail" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Link-Local mac address portion. They are random...
+ actual = re.sub(r"fe80::[0-9a-f:]+", "fe80::XXXX:XXXX:XXXX:XXXX", actual)
+ # Mask out SNPA mac address portion. They are random...
+ actual = re.sub(r"SNPA: [0-9a-f\.]+", "SNPA: XXXX.XXXX.XXXX", actual)
+ # Mask out Circuit ID number
+ actual = re.sub(r"Circuit Id: 0x[0-9a-f]+", "Circuit Id: 0xXX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW ISIS INTERFACE DETAIL",
+ title2="expected SHOW ISIS OSPF6 INTERFACE DETAIL",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed SHOW ISIS INTERFACE DETAIL check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "SHOW ISIS INTERFACE DETAIL failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_bgp_summary():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying BGP Summary")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/show_ip_bgp_summary.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected_original = open(refTableFile).read().rstrip()
+
+ for arguments in [
+ "",
+ "remote-as internal",
+ "remote-as external",
+ "remote-as 100",
+ "remote-as 123",
+ "neighbor 192.168.7.10",
+ "neighbor 192.168.7.10",
+ "neighbor fc00:0:0:8::1000",
+ "neighbor 10.0.0.1",
+ "terse",
+ "remote-as internal terse",
+ "remote-as external terse",
+ "remote-as 100 terse",
+ "remote-as 123 terse",
+ "neighbor 192.168.7.10 terse",
+ "neighbor 192.168.7.10 terse",
+ "neighbor fc00:0:0:8::1000 terse",
+ "neighbor 10.0.0.1 terse",
+ ]:
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd(
+ 'vtysh -c "show ip bgp summary ' + arguments + '" 2> /dev/null'
+ )
+ .rstrip()
+ )
+
+ # Mask out "using XXiXX bytes" portion. They are random...
+ actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
+ # Mask out "using XiXXX KiB" portion. They are random...
+ actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
+
+ # Remove extra summaries which exist with newer versions
+
+ # Remove summary lines (changed recently)
+ actual = re.sub(r"Total number.*", "", actual)
+ actual = re.sub(r"Displayed.*", "", actual)
+ # Remove IPv4 Unicast Summary (Title only)
+ actual = re.sub(r"IPv4 Unicast Summary \(VRF default\):", "", actual)
+ # Remove IPv4 Multicast Summary (all of it)
+ actual = re.sub(r"IPv4 Multicast Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv4 Multicast neighbor is configured", "", actual)
+ # Remove IPv4 VPN Summary (all of it)
+ actual = re.sub(r"IPv4 VPN Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv4 VPN neighbor is configured", "", actual)
+ # Remove IPv4 Encap Summary (all of it)
+ actual = re.sub(r"IPv4 Encap Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv4 Encap neighbor is configured", "", actual)
+ # Remove Unknown Summary (all of it)
+ actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No Unknown neighbor is configured", "", actual)
+ # Make Connect/Active/Idle the same (change them all to Active)
+ actual = re.sub(r" Connect ", " Active ", actual)
+ actual = re.sub(r" Idle ", " Active ", actual)
+
+ actual = re.sub(
+ r"IPv4 labeled-unicast Summary \(VRF default\):", "", actual
+ )
+ actual = re.sub(
+ r"No IPv4 labeled-unicast neighbor is configured", "", actual
+ )
+
+ expected = expected_original
+ # apply argumentss on expected output
+ if "internal" in arguments or "remote-as 100" in arguments:
+ expected = re.sub(r".+\s+200\s+.+", "", expected)
+ elif "external" in arguments:
+ expected = re.sub(r".+\s+100\s+.+Active.+", "", expected)
+ elif "remote-as 123" in arguments:
+ expected = re.sub(
+ r"(192.168.7.(1|2)0|fc00:0:0:8::(1|2)000).+Active.+",
+ "",
+ expected,
+ )
+ expected = re.sub(r"\nNeighbor.+Desc", "", expected)
+ expected = expected + "% No matching neighbor\n"
+ elif "192.168.7.10" in arguments:
+ expected = re.sub(
+ r"(192.168.7.20|fc00:0:0:8::(1|2)000).+Active.+", "", expected
+ )
+ elif "fc00:0:0:8::1000" in arguments:
+ expected = re.sub(
+ r"(192.168.7.(1|2)0|fc00:0:0:8::2000).+Active.+", "", expected
+ )
+ elif "10.0.0.1" in arguments:
+ expected = "No such neighbor in this view/vrf"
+
+ if "terse" in arguments:
+ expected = re.sub(r"BGP table version .+", "", expected)
+ expected = re.sub(r"RIB entries .+", "", expected)
+ expected = re.sub(r"Peers [0-9]+, using .+", "", expected)
+
+ # Strip empty lines
+ actual = actual.lstrip().rstrip()
+ expected = expected.lstrip().rstrip()
+ actual = re.sub(r"\n+", "\n", actual)
+ expected = re.sub(r"\n+", "\n", expected)
+
+ # reapply initial formatting
+ if "terse" in arguments:
+ actual = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", actual)
+ expected = re.sub(r" vrf-id 0\n", " vrf-id 0\n\n", expected)
+ else:
+ actual = re.sub(r"KiB of memory\n", "KiB of memory\n\n", actual)
+ expected = re.sub(r"KiB of memory\n", "KiB of memory\n\n", expected)
+
+ # realign expected neighbor columns if needed
+ try:
+ idx_actual = (
+ re.search(r"(Neighbor\s+V\s+)", actual).group(1).find("V")
+ )
+ idx_expected = (
+ re.search(r"(Neighbor\s+V\s+)", expected).group(1).find("V")
+ )
+ idx_diff = idx_expected - idx_actual
+ if idx_diff > 0:
+ # Neighbor V AS MsgRcvd MsgSent TblVer InQ OutQ Up/Down State/PfxRcd
+ expected = re.sub(" " * idx_diff + "V ", "V ", expected)
+ # 192.168.7.10 4 100 0 0 0 0 0 never Active
+ expected = re.sub(" " * idx_diff + "4 ", "4 ", expected)
+ except AttributeError:
+ pass
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW IP BGP SUMMARY " + arguments.upper(),
+ title2="expected SHOW IP BGP SUMMARY " + arguments.upper(),
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed SHOW IP BGP SUMMARY check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "SHOW IP BGP SUMMARY failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_bgp_ipv6_summary():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying BGP IPv6 Summary")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/show_bgp_ipv6_summary.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show bgp ipv6 summary" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out "using XXiXX bytes" portion. They are random...
+ actual = re.sub(r"using [0-9]+ bytes", "using XXXX bytes", actual)
+ # Mask out "using XiXXX KiB" portion. They are random...
+ actual = re.sub(r"using [0-9]+ KiB", "using XXXX KiB", actual)
+ #
+ # Remove extra summaries which exist with newer versions
+ #
+ # Remove summary lines (changed recently)
+ actual = re.sub(r"Total number.*", "", actual)
+ actual = re.sub(r"Displayed.*", "", actual)
+ # Remove IPv4 Unicast Summary (Title only)
+ actual = re.sub(r"IPv6 Unicast Summary \(VRF default\):", "", actual)
+ # Remove IPv4 Multicast Summary (all of it)
+ actual = re.sub(r"IPv6 Multicast Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv6 Multicast neighbor is configured", "", actual)
+ # Remove IPv4 VPN Summary (all of it)
+ actual = re.sub(r"IPv6 VPN Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv6 VPN neighbor is configured", "", actual)
+ # Remove IPv4 Encap Summary (all of it)
+ actual = re.sub(r"IPv6 Encap Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No IPv6 Encap neighbor is configured", "", actual)
+ # Remove Unknown Summary (all of it)
+ actual = re.sub(r"Unknown Summary \(VRF default\):", "", actual)
+ actual = re.sub(r"No Unknown neighbor is configured", "", actual)
+ # Make Connect/Active/Idle the same (change them all to Active)
+ actual = re.sub(r" Connect ", " Active ", actual)
+ actual = re.sub(r" Idle ", " Active ", actual)
+
+ # Remove Labeled Unicast Summary (all of it)
+ actual = re.sub(
+ r"IPv6 labeled-unicast Summary \(VRF default\):", "", actual
+ )
+ actual = re.sub(
+ r"No IPv6 labeled-unicast neighbor is configured", "", actual
+ )
+
+ # Strip empty lines
+ actual = actual.lstrip()
+ actual = actual.rstrip()
+ #
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW BGP IPv6 SUMMARY",
+ title2="expected SHOW BGP IPv6 SUMMARY",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed SHOW BGP IPv6 SUMMARY check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "SHOW BGP IPv6 SUMMARY failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_nht():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n**** Test that nexthop tracking is at least nominally working ****\n")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ for i in range(1, 2):
+ nhtFile = "%s/r%s/ip_nht.ref" % (thisDir, i)
+ expected = open(nhtFile).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ actual = net["r%s" % i].cmd('vtysh -c "show ip nht" 2> /dev/null').rstrip()
+ actual = re.sub(r"fd [0-9]+", "fd XX", actual)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual `show ip nht`",
+ title2="Expected `show ip nht`",
+ )
+
+ if diff:
+ assert 0, "r%s failed ip nht check:\n%s\n" % (i, diff)
+ else:
+ print("show ip nht is ok\n")
+
+ nhtFile = "%s/r%s/ipv6_nht.ref" % (thisDir, i)
+ expected = open(nhtFile).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ actual = net["r%s" % i].cmd('vtysh -c "show ipv6 nht" 2> /dev/null').rstrip()
+ actual = re.sub(r"fd [0-9]+", "fd XX", actual)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual `show ip nht`",
+ title2="Expected `show ip nht`",
+ )
+
+ if diff:
+ assert 0, "r%s failed ipv6 nht check:\n%s\n" % (i, diff)
+ else:
+ print("show ipv6 nht is ok\n")
+
+
+def test_bgp_ipv4():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying BGP IPv4")
+ print("******************************************\n")
+ diffresult = {}
+ for i in range(1, 2):
+ success = 0
+ for refTableFile in glob.glob("%s/r%s/show_bgp_ipv4*.ref" % (thisDir, i)):
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i].cmd('vtysh -c "show bgp ipv4" 2> /dev/null').rstrip()
+ )
+ # Remove summary line (changed recently)
+ actual = re.sub(r"Total number.*", "", actual)
+ actual = re.sub(r"Displayed.*", "", actual)
+ actual = actual.rstrip()
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW BGP IPv4",
+ title2="expected SHOW BGP IPv4",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ diffresult[refTableFile] = diff
+ else:
+ success = 1
+ print("template %s matched: r%s ok" % (refTableFile, i))
+ break
+
+ if not success:
+ resultstr = "No template matched.\n"
+ for f in diffresult.keys():
+ resultstr += "template %s: r%s failed SHOW BGP IPv4 check:\n%s\n" % (
+ f,
+ i,
+ diffresult[f],
+ )
+ raise AssertionError(
+ "SHOW BGP IPv4 failed for router r%s:\n%s" % (i, resultstr)
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_bgp_ipv6():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying BGP IPv6")
+ print("******************************************\n")
+ diffresult = {}
+ for i in range(1, 2):
+ success = 0
+ for refTableFile in glob.glob("%s/r%s/show_bgp_ipv6*.ref" % (thisDir, i)):
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i].cmd('vtysh -c "show bgp ipv6" 2> /dev/null').rstrip()
+ )
+ # Remove summary line (changed recently)
+ actual = re.sub(r"Total number.*", "", actual)
+ actual = re.sub(r"Displayed.*", "", actual)
+ actual = actual.rstrip()
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW BGP IPv6",
+ title2="expected SHOW BGP IPv6",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ diffresult[refTableFile] = diff
+ else:
+ success = 1
+ print("template %s matched: r%s ok" % (refTableFile, i))
+
+ if not success:
+ resultstr = "No template matched.\n"
+ for f in diffresult.keys():
+ resultstr += "template %s: r%s failed SHOW BGP IPv6 check:\n%s\n" % (
+ f,
+ i,
+ diffresult[f],
+ )
+ raise AssertionError(
+ "SHOW BGP IPv6 failed for router r%s:\n%s" % (i, resultstr)
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_route_map():
+ global fatal_error
+ net = get_topogen().net
+
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying some basic routemap forward references\n")
+ print("*******************************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refroutemap = "%s/r%s/show_route_map.ref" % (thisDir, i)
+ if os.path.isfile(refroutemap):
+ expected = open(refroutemap).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ actual = (
+ net["r%s" % i].cmd('vtysh -c "show route-map" 2> /dev/null').rstrip()
+ )
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual show route-map",
+ title2="expected show route-map",
+ )
+
+ if diff:
+ sys.stderr.write(
+ "r%s failed show route-map command Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "Show route-map command failed for router r%s:\n%s" % (i, diff)
+
+
+def test_nexthop_groups_with_route_maps():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Verifying Nexthop Groups With Route-Maps")
+ print("******************************************\n")
+
+ ### Nexthop Group With Route-Map Tests
+
+ # Create a lib nexthop-group
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group test" -c "nexthop 1.1.1.1" -c "nexthop 1.1.1.2"'
+ )
+
+ ## Route-Map Proto Source
+
+ route_str = "2.2.2.1"
+ src_str = "192.168.0.1"
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "route-map NH-SRC permit 111" -c "set src %s"' % src_str
+ )
+ net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NH-SRC"')
+
+ net["r1"].cmd('vtysh -c "sharp install routes %s nexthop-group test 1"' % route_str)
+
+ verify_route_nexthop_group("%s/32" % route_str)
+
+ # Only a valid test on linux using nexthop objects
+ if sys.platform.startswith("linux"):
+ output = net["r1"].cmd("ip route show %s/32" % route_str)
+ match = re.search(r"src %s" % src_str, output)
+ assert match is not None, "Route %s/32 not installed with src %s" % (
+ route_str,
+ src_str,
+ )
+
+ # Remove NHG routes and route-map
+ net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % route_str)
+ net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NH-SRC"')
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "no route-map NH-SRC permit 111" # -c "set src %s"' % src_str
+ )
+ net["r1"].cmd('vtysh -c "c t" -c "no route-map NH-SRC"')
+
+ ## Route-Map Deny/Permit with same nexthop group
+
+ permit_route_str = "3.3.3.1"
+ deny_route_str = "3.3.3.2"
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "ip prefix-list NOPE seq 5 permit %s/32"' % permit_route_str
+ )
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "route-map NOPE permit 111" -c "match ip address prefix-list NOPE"'
+ )
+ net["r1"].cmd('vtysh -c "c t" -c "route-map NOPE deny 222"')
+ net["r1"].cmd('vtysh -c "c t" -c "ip protocol sharp route-map NOPE"')
+
+ # This route should be permitted
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes %s nexthop-group test 1"' % permit_route_str
+ )
+
+ verify_route_nexthop_group("%s/32" % permit_route_str)
+
+ # This route should be denied
+ net["r1"].cmd(
+ 'vtysh -c "sharp install routes %s nexthop-group test 1"' % deny_route_str
+ )
+
+ nhg_id = route_get_nhg_id(deny_route_str)
+ output = net["r1"].cmd('vtysh -c "show nexthop-group rib %d"' % nhg_id)
+
+ match = re.search(r"Valid", output)
+ assert match is None, "Nexthop Group ID=%d should not be marked Valid" % nhg_id
+
+ match = re.search(r"Installed", output)
+ assert match is None, "Nexthop Group ID=%d should not be marked Installed" % nhg_id
+
+ # Remove NHG routes and route-map
+ net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % permit_route_str)
+ net["r1"].cmd('vtysh -c "sharp remove routes %s 1"' % deny_route_str)
+ net["r1"].cmd('vtysh -c "c t" -c "no ip protocol sharp route-map NOPE"')
+ net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE permit 111"')
+ net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE deny 222"')
+ net["r1"].cmd('vtysh -c "c t" -c "no route-map NOPE"')
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "no ip prefix-list NOPE seq 5 permit %s/32"'
+ % permit_route_str
+ )
+
+
+def test_nexthop_group_replace():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Verifying Nexthop Groups")
+ print("******************************************\n")
+
+ ### Nexthop Group Tests
+
+ ## 2-Way ECMP Directly Connected
+
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group replace" -c "nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.2 r1-eth2 onlink"'
+ )
+
+ # At the moment there is absolutely no real easy way to query sharpd
+ # for the nexthop group actually installed. If it is not installed
+ # sharpd will just transmit the nexthops down instead of the nexthop
+ # group id. Leading to a situation where the replace is not actually
+ # being tested. So let's just wait some time here because this
+ # is hard and this test fails all the time
+ sleep(5)
+
+ # Create with sharpd using nexthop-group
+ net["r1"].cmd('vtysh -c "sharp install routes 3.3.3.1 nexthop-group replace 1"')
+
+ verify_route_nexthop_group("3.3.3.1/32")
+
+ # Change the nexthop group
+ net["r1"].cmd(
+ 'vtysh -c "c t" -c "nexthop-group replace" -c "no nexthop 1.1.1.1 r1-eth1 onlink" -c "nexthop 1.1.1.3 r1-eth1 onlink" -c "nexthop 1.1.1.4 r1-eth4 onlink"'
+ )
+
+ # Verify it updated. We can just check install and ecmp count here.
+ verify_route_nexthop_group("3.3.3.1/32", False, 3)
+
+
+def test_mpls_interfaces():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ # Skip if no LDP installed or old kernel
+ if net["r1"].daemon_available("ldpd") == False:
+ pytest.skip("No MPLS or kernel < 4.5")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying MPLS Interfaces")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 2):
+ refTableFile = "%s/r%s/show_mpls_ldp_interface.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Timer in Uptime
+ actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS LDP interface status",
+ title2="expected MPLS LDP interface status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ if failures > 0:
+ fatal_error = "MPLS LDP Interface status failed"
+
+ assert (
+ failures == 0
+ ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 2):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_shutdown_check_stderr():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Verifying unexpected STDERR output from daemons")
+ print("******************************************\n")
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ print(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("thisDir=" + thisDir)
+
+ net["r1"].stopRouter()
+
+ log = net["r1"].getStdErr("ripd")
+ if log:
+ print("\nRIPd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("ripngd")
+ if log:
+ print("\nRIPngd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("ospfd")
+ if log:
+ print("\nOSPFd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("ospf6d")
+ if log:
+ print("\nOSPF6d StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("isisd")
+ if log:
+ print("\nISISd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("bgpd")
+ if log:
+ print("\nBGPd StdErr Log:\n" + log)
+
+ log = net["r1"].getStdErr("nhrpd")
+ if log:
+ print("\nNHRPd StdErr Log:\n" + log)
+
+ log = net["r1"].getStdErr("pbrd")
+ if log:
+ print("\nPBRd StdErr Log:\n" + log)
+
+ log = net["r1"].getStdErr("babeld")
+ if log:
+ print("\nBABELd StdErr Log:\n" + log)
+
+ if net["r1"].daemon_available("ldpd"):
+ log = net["r1"].getStdErr("ldpd")
+ if log:
+ print("\nLDPd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("zebra")
+ if log:
+ print("\nZebra StdErr Log:\n" + log)
+
+
+def test_shutdown_check_memleak():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
+ print(
+ "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
+ )
+ pytest.skip("Skipping test for memory leaks")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ for i in range(1, 2):
+ net["r%s" % i].stopRouter()
+ net["r%s" % i].report_memory_leaks(
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
+ )
+
+
+if __name__ == "__main__":
+ # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/analyze.py b/tests/topotests/analyze.py
new file mode 100755
index 0000000..108bd1b
--- /dev/null
+++ b/tests/topotests/analyze.py
@@ -0,0 +1,271 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+import argparse
+import glob
+import logging
+import os
+import re
+import subprocess
+import sys
+from collections import OrderedDict
+
+import xmltodict
+
+
+def get_summary(results):
+ ntest = int(results["@tests"])
+ nfail = int(results["@failures"])
+ nerror = int(results["@errors"])
+ nskip = int(results["@skipped"])
+ npass = ntest - nfail - nskip - nerror
+ return ntest, npass, nfail, nerror, nskip
+
+
+def print_summary(results, args):
+ ntest, npass, nfail, nerror, nskip = (0, 0, 0, 0, 0)
+ for group in results:
+ _ntest, _npass, _nfail, _nerror, _nskip = get_summary(results[group])
+ if args.verbose:
+ print(
+ f"Group: {group} Total: {_ntest} PASSED: {_npass}"
+ " FAIL: {_nfail} ERROR: {_nerror} SKIP: {_nskip}"
+ )
+ ntest += _ntest
+ npass += _npass
+ nfail += _nfail
+ nerror += _nerror
+ nskip += _nskip
+ print(f"Total: {ntest} PASSED: {npass} FAIL: {nfail} ERROR: {nerror} SKIP: {nskip}")
+
+
+def get_global_testcase(results):
+ for group in results:
+ for testcase in results[group]["testcase"]:
+ if "@file" not in testcase:
+ return testcase
+ return None
+
+
+def get_filtered(tfilters, results, args):
+ if isinstance(tfilters, str) or tfilters is None:
+ tfilters = [tfilters]
+ found_files = OrderedDict()
+ for group in results:
+ if isinstance(results[group]["testcase"], list):
+ tlist = results[group]["testcase"]
+ else:
+ tlist = [results[group]["testcase"]]
+ for testcase in tlist:
+ for tfilter in tfilters:
+ if tfilter is None:
+ if (
+ "failure" not in testcase
+ and "error" not in testcase
+ and "skipped" not in testcase
+ ):
+ break
+ elif tfilter in testcase:
+ break
+ else:
+ continue
+ # cname = testcase["@classname"]
+ fname = testcase.get("@file", "")
+ cname = testcase.get("@classname", "")
+ if not fname and not cname:
+ name = testcase.get("@name", "")
+ if not name:
+ continue
+ # If we had a failure at the module level we could be here.
+ fname = name.replace(".", "/") + ".py"
+ tcname = fname
+ else:
+ if not fname:
+ fname = cname.replace(".", "/") + ".py"
+ if args.files_only or "@name" not in testcase:
+ tcname = fname
+ else:
+ tcname = fname + "::" + testcase["@name"]
+ found_files[tcname] = testcase
+ return found_files
+
+
+def dump_testcase(testcase):
+ expand_keys = ("failure", "error", "skipped")
+
+ s = ""
+ for key, val in testcase.items():
+ if isinstance(val, str) or isinstance(val, float) or isinstance(val, int):
+ s += "{}: {}\n".format(key, val)
+ elif isinstance(val, list):
+ for k2, v2 in enumerate(val):
+ s += "{}: {}\n".format(k2, v2)
+ else:
+ for k2, v2 in val.items():
+ s += "{}: {}\n".format(k2, v2)
+ return s
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ "-A",
+ "--save",
+ action="store_true",
+ help="Save /tmp/topotests{,.xml} in --rundir if --rundir does not yet exist",
+ )
+ parser.add_argument(
+ "-F",
+ "--files-only",
+ action="store_true",
+ help="print test file names rather than individual full testcase names",
+ )
+ parser.add_argument(
+ "-S",
+ "--select",
+ default="fe",
+ help="select results combination of letters: 'e'rrored 'f'ailed 'p'assed 's'kipped.",
+ )
+ parser.add_argument(
+ "-r",
+ "--results",
+ help="xml results file or directory containing xml results file",
+ )
+ parser.add_argument("--rundir", help=argparse.SUPPRESS)
+ parser.add_argument(
+ "-E",
+ "--enumerate",
+ action="store_true",
+ help="enumerate each item (results scoped)",
+ )
+ parser.add_argument("-T", "--test", help="print testcase at enumeration")
+ parser.add_argument(
+ "--errmsg", action="store_true", help="print testcase error message"
+ )
+ parser.add_argument(
+ "--errtext", action="store_true", help="print testcase error text"
+ )
+ parser.add_argument("--time", action="store_true", help="print testcase run times")
+
+ parser.add_argument("-s", "--summary", action="store_true", help="print summary")
+ parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ args = parser.parse_args()
+
+ if args.save and args.results and not os.path.exists(args.results):
+ if not os.path.exists("/tmp/topotests"):
+ logging.critical('No "/tmp/topotests" directory to save')
+ sys.exit(1)
+ subprocess.run(["mv", "/tmp/topotests", args.results])
+ # # Old location for results
+ # if os.path.exists("/tmp/topotests.xml", args.results):
+ # subprocess.run(["mv", "/tmp/topotests.xml", args.results])
+
+ assert (
+ args.test is None or not args.files_only
+ ), "Can't have both --files and --test"
+
+ results = {}
+ ttfiles = []
+ if args.rundir:
+ basedir = os.path.realpath(args.rundir)
+ os.chdir(basedir)
+
+ newfiles = glob.glob("tt-group-*/topotests.xml")
+ if newfiles:
+ ttfiles.extend(newfiles)
+ if os.path.exists("topotests.xml"):
+ ttfiles.append("topotests.xml")
+ else:
+ if args.results:
+ if os.path.exists(os.path.join(args.results, "topotests.xml")):
+ args.results = os.path.join(args.results, "topotests.xml")
+ if not os.path.exists(args.results):
+ logging.critical("%s doesn't exist", args.results)
+ sys.exit(1)
+ ttfiles = [args.results]
+ elif os.path.exists("/tmp/topotests/topotests.xml"):
+ ttfiles.append("/tmp/topotests/topotests.xml")
+
+ if not ttfiles:
+ if os.path.exists("/tmp/topotests.xml"):
+ ttfiles.append("/tmp/topotests.xml")
+
+ for f in ttfiles:
+ m = re.match(r"tt-group-(\d+)/topotests.xml", f)
+ group = int(m.group(1)) if m else 0
+ with open(f) as xml_file:
+ results[group] = xmltodict.parse(xml_file.read())["testsuites"]["testsuite"]
+
+ filters = []
+ if "e" in args.select:
+ filters.append("error")
+ if "f" in args.select:
+ filters.append("failure")
+ if "s" in args.select:
+ filters.append("skipped")
+ if "p" in args.select:
+ filters.append(None)
+
+ found_files = get_filtered(filters, results, args)
+ if found_files:
+ if args.test is not None:
+ if args.test == "all":
+ keys = found_files.keys()
+ else:
+ keys = [list(found_files.keys())[int(args.test)]]
+ for key in keys:
+ testcase = found_files[key]
+ if args.errtext:
+ if "error" in testcase:
+ errmsg = testcase["error"]["#text"]
+ elif "failure" in testcase:
+ errmsg = testcase["failure"]["#text"]
+ else:
+ errmsg = "none found"
+ s = "{}: {}".format(key, errmsg)
+ elif args.time:
+ text = testcase["@time"]
+ s = "{}: {}".format(text, key)
+ elif args.errmsg:
+ if "error" in testcase:
+ errmsg = testcase["error"]["@message"]
+ elif "failure" in testcase:
+ errmsg = testcase["failure"]["@message"]
+ else:
+ errmsg = "none found"
+ s = "{}: {}".format(key, errmsg)
+ else:
+ s = dump_testcase(testcase)
+ print(s)
+ elif filters:
+ if args.enumerate:
+ print(
+ "\n".join(["{} {}".format(i, x) for i, x in enumerate(found_files)])
+ )
+ else:
+ print("\n".join(found_files))
+
+ if args.summary:
+ print_summary(results, args)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/__init__.py b/tests/topotests/bfd_bgp_cbit_topo3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/__init__.py
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json
new file mode 100644
index 0000000..5cba71e
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgp_ipv6_routes_down.json
@@ -0,0 +1,98 @@
+{
+ "vrfName": "default",
+ "routerId": "10.254.254.1",
+ "localAS": 101,
+ "routes":
+ {
+ "2001:db8:6::/64": [
+ {
+ "stale": true,
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:6::",
+ "prefixLen": 64,
+ "network": "2001:db8:6::\/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001:db8:4::1",
+ "origin": "IGP",
+ "nexthops": [
+ { "ip": "2001:db8:4::1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [
+ {
+ "stale": true,
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:7::",
+ "prefixLen": 64, "network":
+ "2001:db8:7::\/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001:db8:4::1",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "2001:db8:4::1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:8::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:8::",
+ "prefixLen": 64,
+ "network": "2001:db8:8::\/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:9::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:9::",
+ "prefixLen": 64,
+ "network": "2001:db8:9::\/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
+
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf
new file mode 100644
index 0000000..d30c320
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/bgpd.conf
@@ -0,0 +1,23 @@
+! debug bgp neighbor-events
+router bgp 101
+ bgp router-id 10.254.254.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 8 24
+ bgp graceful-restart
+ neighbor 2001:db8:4::1 remote-as 102
+ neighbor 2001:db8:4::1 timers 3 10
+ neighbor 2001:db8:4::1 remote-as external
+ neighbor 2001:db8:4::1 bfd
+ neighbor 2001:db8:4::1 bfd check-control-plane-failure
+ neighbor 2001:db8:4::1 update-source 2001:db8:1::1
+ neighbor 2001:db8:4::1 ebgp-multihop 5
+ address-family ipv4 unicast
+ no neighbor 2001:db8:4::1 activate
+ exit-address-family
+ address-family ipv6 unicast
+ network 2001:db8:8::/64
+ network 2001:db8:9::/64
+ neighbor 2001:db8:4::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json
new file mode 100644
index 0000000..8eea183
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/ipv6_routes.json
@@ -0,0 +1,80 @@
+{
+ "2001:db8:1::/64": [{
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [{
+ "directlyConnected": true,
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:4::/64": [{
+ "distance": 1,
+ "protocol": "static",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [{
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "2001:db8:6::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:6::/64",
+ "nexthops": [{
+ "ip":"2001:db8:4::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:1::2",
+ "afi": "ipv6",
+ "interfaceName": "r1-eth0"
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:7::/64",
+ "nexthops": [{
+ "ip":"2001:db8:4::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive": true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:1::2",
+ "afi": "ipv6",
+ "interfaceName":"r1-eth0"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json
new file mode 100644
index 0000000..b436d55
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:4::1",
+ "local":"2001:db8:1::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json
new file mode 100644
index 0000000..4984b52
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/peers_down.json
@@ -0,0 +1,15 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:4::1",
+ "local":"2001:db8:1::1",
+ "status":"init",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf
new file mode 100644
index 0000000..3a30cd4
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r1/zebra.conf
@@ -0,0 +1,8 @@
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ipv6 address 2001:db8:1::1/64
+!
+ipv6 route 2001:db8:4::/64 2001:db8:1::2
+
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf
new file mode 100644
index 0000000..0f70be1
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r2/zebra.conf
@@ -0,0 +1,9 @@
+ip forwarding
+ipv6 forwarding
+!
+interface r2-eth0
+ ipv6 address 2001:db8:1::2/64
+!
+interface r2-eth1
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json
new file mode 100644
index 0000000..c0cb3c4
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgp_ipv6_routes_down.json
@@ -0,0 +1,52 @@
+{
+ "vrfName": "default",
+ "routerId": "10.254.254.3",
+ "localAS": 102,
+ "routes":
+ {
+ "2001:db8:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:6::",
+ "prefixLen": 64,
+ "network": "2001:db8:6::\/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:7::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "2001:db8:7::",
+ "prefixLen": 64,
+ "network": "2001:db8:7::\/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "::",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf
new file mode 100644
index 0000000..22f0650
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/bgpd.conf
@@ -0,0 +1,29 @@
+! debug bgp neighbor-events
+router bgp 102
+ bgp router-id 10.254.254.3
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 20 60
+ bgp graceful-restart
+ ! simulate NSF machine
+ bgp graceful-restart preserve-fw-state
+ bgp graceful-restart stalepath-time 900
+ bgp graceful-restart restart-time 900
+ neighbor 2001:db8:1::1 remote-as 101
+ neighbor 2001:db8:1::1 timers 3 10
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 update-source 2001:db8:4::1
+ neighbor 2001:db8:1::1 bfd
+ neighbor 2001:db8:1::1 bfd check-control-plane-failure
+ neighbor 2001:db8:1::1 ebgp-multihop 5
+ !
+ address-family ipv4 unicast
+ no neighbor 2001:db8:1::1 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ neighbor 2001:db8:1::1 activate
+ network 2001:db8:6::/64
+ network 2001:db8:7::/64
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json
new file mode 100644
index 0000000..09808cc
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/ipv6_routes.json
@@ -0,0 +1,80 @@
+{
+ "2001:db8:1::/64": [{
+ "distance": 1,
+ "protocol": "static",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [{
+ "interfaceName": "r3-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "2001:db8:4::/64": [{
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [{
+ "directlyConnected": true,
+ "interfaceName": "r3-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:8::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:8::/64",
+ "nexthops": [{
+ "ip":"2001:db8:1::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:4::2",
+ "afi": "ipv6",
+ "interfaceName":"r3-eth0"
+ }
+ ]
+ }
+ ],
+ "2001:db8:9::/64": [{
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:9::/64",
+ "nexthops": [{
+ "ip":"2001:db8:1::1",
+ "active": true,
+ "afi": "ipv6",
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"2001:db8:4::2",
+ "afi": "ipv6",
+ "interfaceName":"r3-eth0"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json
new file mode 100644
index 0000000..fc9e145
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:1::1",
+ "local":"2001:db8:4::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json
new file mode 100644
index 0000000..620c6dd
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/peers_down.json
@@ -0,0 +1,15 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:1::1",
+ "local":"2001:db8:4::1",
+ "status":"down",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf b/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf
new file mode 100644
index 0000000..7759251
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/r3/zebra.conf
@@ -0,0 +1,7 @@
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ipv6 address 2001:db8:4::1/64
+!
+ipv6 route 2001:db8:1::/64 2001:db8:4::2
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot
new file mode 100644
index 0000000..270de82
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.dot
@@ -0,0 +1,58 @@
+## 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-topo2";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+ r2 -- sw2 [label="eth1"];
+ r3 -- sw2 [label="eth0"];
+}
diff --git a/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
new file mode 100644
index 0000000..9243266
--- /dev/null
+++ b/tests/topotests/bfd_bgp_cbit_topo3/test_bfd_bgp_cbit_topo3.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_bgp_cbit_topo3.py
+#
+# Copyright (c) 2019 6WIND
+#
+# 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_bgp_cbit_topo3.py: Test the FRR BFD daemon with multihop and BGP
+unnumbered.
+"""
+
+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
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.bfdd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r2", "r3"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # Verify that we are using the proper version and that the BFD
+ # daemon exists.
+ for router in router_list.values():
+ # Check for Version
+ if router.has_version("<", "5.1"):
+ tgen.set_error("Unsupported FRR version")
+ break
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged before checking for the BFD
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv6 routing tables.
+ logger.info("Checking IPv6 routes for convergence")
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ipv6 route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bfd peers to go up")
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bfd_loss_intermediate():
+ """
+ Assert that BFD notices the bfd link down failure.
+ but BGP entries should still be present
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("removing IPv6 address from r2 to simulate loss of connectivity")
+ # Disable r2-eth0 ipv6 address
+ cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "no ipv6 address 2001:db8:4::2/64"'
+ tgen.net["r2"].cmd(cmd)
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info("waiting for BFD converge down")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ json_file = "{}/{}/peers_down.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=32, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info("waiting for BGP entries to become stale")
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ json_file = "{}/{}/bgp_ipv6_routes_down.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp ipv6 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ logger.info("Checking IPv6 routes on r1 should still be present")
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ if router.name == "r3":
+ continue
+ json_file = "{}/r1/ipv6_routes.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ipv6 route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bfd_comes_back_again():
+ """
+ Assert that BFD notices the bfd link up
+ and that ipv6 entries appear back
+ """
+ tgen = get_topogen()
+ logger.info("re-adding IPv6 address from r2 to simulate connectivity is back")
+ # adds back r2-eth0 ipv6 address
+ cmd = 'vtysh -c "configure terminal" -c "interface r2-eth1" -c "ipv6 address 2001:db8:4::2/64"'
+ tgen.net["r2"].cmd(cmd)
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info("waiting for BFD to converge up")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ if router.name == "r2":
+ continue
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=16, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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/bfd_isis_topo1/__init__.py b/tests/topotests/bfd_isis_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/__init__.py
diff --git a/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf
new file mode 100644
index 0000000..dbcf23f
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/bfdd.conf
@@ -0,0 +1,19 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 10.0.1.2 interface eth-rt2
+ 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/rt1/isisd.conf b/tests/topotests/bfd_isis_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..a5cbdd9
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/isisd.conf
@@ -0,0 +1,36 @@
+log file isisd.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis adj-packets
+! debug isis lsp-sched
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis bfd
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis bfd
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..af6e45c
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..68d3fe2
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..cb4083d
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step2/show_bfd_peers.ref
@@ -0,0 +1,16 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref
new file mode 100644
index 0000000..cb4083d
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_healthy.ref
@@ -0,0 +1,16 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref
new file mode 100644
index 0000000..f00b9f3
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt2_down.ref
@@ -0,0 +1,9 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref
new file mode 100644
index 0000000..f5bd276
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_bfd_peers_rt3_down.ref
@@ -0,0 +1,9 @@
+[
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref
new file mode 100644
index 0000000..af6e45c
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_healthy.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref
new file mode 100644
index 0000000..b8366bc
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt2_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref
new file mode 100644
index 0000000..42bd6ab
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ip_route_rt3_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref
new file mode 100644
index 0000000..68d3fe2
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_healthy.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref
new file mode 100644
index 0000000..200053c
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt2_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref
new file mode 100644
index 0000000..4297f16
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/step3/show_ipv6_route_rt3_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_isis_topo1/rt1/zebra.conf b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..7e6f788
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt1/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+log timestamp precision 3
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra events
+! debug zebra rib
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address ::ffff:0101:0101/128
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+interface eth-rt3
+ ip address 10.0.2.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf
new file mode 100644
index 0000000..d5054aa
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt2/bfdd.conf
@@ -0,0 +1,13 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 10.0.1.1 interface eth-rt1
+ detect-multiplier 3
+ receive-interval 300
+ transmit-interval 300
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt2/isisd.conf b/tests/topotests/bfd_isis_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..b32170d
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt2/isisd.conf
@@ -0,0 +1,31 @@
+log file isisd.log
+!
+hostname rt2
+!
+password 1
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis bfd
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..8a90649
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt2/step2/show_bfd_peers.ref
@@ -0,0 +1,9 @@
+[
+ {
+ "peer": "10.0.1.1",
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt2/zebra.conf b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..5788e31
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address ::ffff:0202:0202/128
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt5
+ ip address 10.0.3.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf
new file mode 100644
index 0000000..fd9a5e1
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt3/bfdd.conf
@@ -0,0 +1,13 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 10.0.2.1 interface eth-rt1
+ detect-multiplier 3
+ receive-interval 300
+ transmit-interval 300
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt3/isisd.conf b/tests/topotests/bfd_isis_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..b98f249
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt3/isisd.conf
@@ -0,0 +1,33 @@
+log file isisd.log
+!
+hostname rt3
+!
+password 1
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis bfd
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+!
+
diff --git a/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..13eb2a2
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt3/step2/show_bfd_peers.ref
@@ -0,0 +1,9 @@
+[
+ {
+ "peer": "10.0.2.1",
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_isis_topo1/rt3/zebra.conf b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..78eac2e
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address ::ffff:0303:0303/128
+!
+interface eth-rt1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4
+ ip address 10.0.4.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt4/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt4/isisd.conf b/tests/topotests/bfd_isis_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..6a4b05f
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt4/isisd.conf
@@ -0,0 +1,30 @@
+log file isisd.log
+!
+hostname rt4
+!
+password 1
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt4/zebra.conf b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..a6cb573
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address ::ffff:0404:0404/128
+!
+interface eth-rt3
+ ip address 10.0.4.2/24
+!
+interface eth-rt5
+ ip address 10.0.5.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf b/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt5/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt5/isisd.conf b/tests/topotests/bfd_isis_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..ed32b15
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt5/isisd.conf
@@ -0,0 +1,30 @@
+log file isisd.log
+!
+hostname rt5
+!
+password 1
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+!
diff --git a/tests/topotests/bfd_isis_topo1/rt5/zebra.conf b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..33473c9
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/rt5/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address ::ffff:0505:0505/128
+!
+interface eth-rt2
+ ip address 10.0.3.2/24
+!
+interface eth-rt4
+ ip address 10.0.5.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py b/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py
new file mode 100644
index 0000000..27a4419
--- /dev/null
+++ b/tests/topotests/bfd_isis_topo1/test_bfd_isis_topo1.py
@@ -0,0 +1,268 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_isis_topo1.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_isis_topo1.py:
+
+ +---------+
+ | |
+ eth-rt2 (.1) | RT1 | eth-rt3 (.1)
+ +----------+ 1.1.1.1 +----------+
+ | | | |
+ | +---------+ |
+ | |
+ | 10.0.2.0/24 |
+ | |
+ | eth-rt1 | (.2)
+ | 10.0.1.0/24 +----+----+
+ | | |
+ | | RT3 |
+ | | 3.3.3.3 |
+ | | |
+ (.2) | eth-rt1 +----+----+
+ +----+----+ eth-rt4 | (.1)
+ | | |
+ | RT2 | |
+ | 2.2.2.2 | 10.0.4.0/24 |
+ | | |
+ +----+----+ |
+ (.1) | eth-rt5 eth-rt3 | (.2)
+ | +----+----+
+ | | |
+ | | RT4 |
+ | | 4.4.4.4 |
+ | | |
+ | +----+----+
+ | 10.0.3.0/24 eth-rt5 | (.1)
+ | |
+ | |
+ | 10.0.5.0/24 |
+ | |
+ | +---------+ |
+ | | | |
+ +----------+ RT5 +----------+
+ eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2)
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.isisd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("rt1:eth-rt2", "rt2:eth-rt1"),
+ "s2": ("rt1:eth-rt3", "rt3:eth-rt1"),
+ "s3": ("rt2:eth-rt5", "rt5:eth-rt2"),
+ "s4": ("rt3:eth-rt4", "rt4:eth-rt3"),
+ "s5": ("rt4:eth-rt5", "rt5:eth-rt4"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ 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 print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def router_compare_json_output(rname, command, reference, count=120, wait=0.5):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+## TEST STEPS
+
+
+def test_rib_isis_step1():
+ logger.info("Test (step 1): verify RIB (IPv4 and IPv6) for IS-IS")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router_compare_json_output(
+ "rt1", "show ip route isis json", "step1/show_ip_route.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route isis json", "step1/show_ipv6_route.ref"
+ )
+
+
+def test_bfd_isis_sessions_step2():
+ logger.info("Test (step 2): verify BFD peers for IS-IS")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # BFD is just used on three routers
+ for rt in ["rt1", "rt2", "rt3"]:
+ router_compare_json_output(
+ rt, "show bfd peers json", "step2/show_bfd_peers.ref"
+ )
+
+
+def test_bfd_isis_interface_failure_rt2_step3():
+ logger.info("Test (step 2): check failover handling when RT2 goes down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ # TODO: add check for array size
+ router_compare_json_output(
+ "rt1", "show ip route isis json", "step3/show_ip_route_rt2_down.ref", 20, 1
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt2_down.ref", 20, 1
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 20, 1
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+def test_bfd_isis_interface_failure_rt3_step3():
+ logger.info("Test (step 2): check failover handling when RT2 goes down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ # TODO: add check for array size
+ router_compare_json_output(
+ "rt1", "show ip route isis json", "step3/show_ip_route_rt3_down.ref", 20, 1
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_rt3_down.ref", 20, 1
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 20, 1
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route isis json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route isis json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+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/bfd_ospf_topo1/__init__.py b/tests/topotests/bfd_ospf_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/__init__.py
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf
new file mode 100644
index 0000000..f34f4ca
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/bfdd.conf
@@ -0,0 +1,9 @@
+log file bfdd.log
+log timestamp precision 3
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf
new file mode 100644
index 0000000..98da8c2
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/ospf6d.conf
@@ -0,0 +1,25 @@
+log file ospf6d.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt3
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+router ospf6
+ ospf6 router-id 1.1.1.1
+ interface eth-rt2 area 0.0.0.0
+ interface eth-rt3 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..ce36494
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/ospfd.conf
@@ -0,0 +1,32 @@
+log file ospfd.log
+log timestamp precision 3
+!
+hostname rt1
+!
+password 1
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+ ip ospf bfd
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 1.1.1.1
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..f354eff
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..6465efb
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..63f0d50
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step2/show_bfd_peers.ref
@@ -0,0 +1,26 @@
+[
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref
new file mode 100644
index 0000000..42051f9
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_healthy.ref
@@ -0,0 +1,28 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref
new file mode 100644
index 0000000..d844ee6
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt2_down.ref
@@ -0,0 +1,15 @@
+[
+ {
+ "peer": "10.0.2.2",
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt3",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref
new file mode 100644
index 0000000..3279908
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_bfd_peers_rt3_down.ref
@@ -0,0 +1,15 @@
+[
+ {
+ "peer": "10.0.1.2",
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt2",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref
new file mode 100644
index 0000000..f354eff
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_healthy.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref
new file mode 100644
index 0000000..43eecd0
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt2_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref
new file mode 100644
index 0000000..409af63
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ip_route_rt3_down.ref
@@ -0,0 +1,74 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref
new file mode 100644
index 0000000..6465efb
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_healthy.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref
new file mode 100644
index 0000000..cfb1ef1
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt2_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref
new file mode 100644
index 0000000..58b44da
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/step3/show_ipv6_route_rt3_down.ref
@@ -0,0 +1,70 @@
+{
+ "::ffff:202:202\/128":[
+ {
+ "prefix":"::ffff:202:202\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:303:303\/128":[
+ {
+ "prefix":"::ffff:303:303\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:404:404\/128":[
+ {
+ "prefix":"::ffff:404:404\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "::ffff:505:505\/128":[
+ {
+ "prefix":"::ffff:505:505\/128",
+ "protocol":"ospf6",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..7e6f788
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt1/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+log timestamp precision 3
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra events
+! debug zebra rib
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address ::ffff:0101:0101/128
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+interface eth-rt3
+ ip address 10.0.2.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf
new file mode 100644
index 0000000..5baea3c
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/bfdd.conf
@@ -0,0 +1,7 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf
new file mode 100644
index 0000000..34b0902
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/ospf6d.conf
@@ -0,0 +1,23 @@
+log file ospf6d.log
+!
+hostname rt2
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt5
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 2.2.2.2
+ interface eth-rt1 area 0.0.0.0
+ interface eth-rt5 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..a8ca564
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/ospfd.conf
@@ -0,0 +1,30 @@
+log file ospfd.log
+!
+hostname rt2
+!
+password 1
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+ ip ospf bfd
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+router ospf
+ ospf router-id 2.2.2.2
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..d6df1eb
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/step2/show_bfd_peers.ref
@@ -0,0 +1,14 @@
+[
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..5788e31
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address ::ffff:0202:0202/128
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt5
+ ip address 10.0.3.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf
new file mode 100644
index 0000000..5baea3c
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/bfdd.conf
@@ -0,0 +1,7 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf
new file mode 100644
index 0000000..8ab4eee
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/ospf6d.conf
@@ -0,0 +1,23 @@
+log file ospf6d.log
+!
+hostname rt3
+!
+password 1
+!
+interface eth-rt1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 bfd
+!
+interface eth-rt4
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 3.3.3.3
+ interface eth-rt1 area 0.0.0.0
+ interface eth-rt4 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..0404994
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/ospfd.conf
@@ -0,0 +1,30 @@
+log file ospfd.log
+!
+hostname rt3
+!
+password 1
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt1
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+ ip ospf bfd
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+router ospf
+ ospf router-id 3.3.3.3
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref b/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref
new file mode 100644
index 0000000..d6df1eb
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/step2/show_bfd_peers.ref
@@ -0,0 +1,14 @@
+[
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ },
+ {
+ "interface": "eth-rt1",
+ "status": "up",
+ "diagnostic": "ok",
+ "remote-diagnostic": "ok"
+ }
+]
diff --git a/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..78eac2e
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address ::ffff:0303:0303/128
+!
+interface eth-rt1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4
+ ip address 10.0.4.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt4/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf
new file mode 100644
index 0000000..138b688
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt4/ospf6d.conf
@@ -0,0 +1,22 @@
+log file ospf6d.log
+!
+hostname rt4
+!
+password 1
+!
+interface eth-rt3
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+!
+interface eth-rt5
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+ ipv6 ospf6 network broadcast
+!
+router ospf6
+ ospf6 router-id 4.4.4.4
+ interface eth-rt3 area 0.0.0.0
+ interface eth-rt5 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..6b8ab37
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt4/ospfd.conf
@@ -0,0 +1,29 @@
+log file ospfd.log
+!
+hostname rt4
+!
+password 1
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt3
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt5
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+router ospf
+ ospf router-id 4.4.4.4
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..a6cb573
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address ::ffff:0404:0404/128
+!
+interface eth-rt3
+ ip address 10.0.4.2/24
+!
+interface eth-rt5
+ ip address 10.0.5.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf b/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt5/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf
new file mode 100644
index 0000000..6eb4fe5
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt5/ospf6d.conf
@@ -0,0 +1,22 @@
+log file ospf6d.log
+!
+hostname rt5
+!
+password 1
+!
+interface eth-rt2
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+!
+interface eth-rt4
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 8
+!
+router ospf6
+ ospf6 router-id 5.5.5.5
+ interface eth-rt2 area 0.0.0.0
+ interface eth-rt4 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf b/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000..043432e
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt5/ospfd.conf
@@ -0,0 +1,29 @@
+log file ospfd.log
+!
+hostname rt5
+!
+password 1
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt2
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+interface eth-rt4
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 8
+!
+router ospf
+ ospf router-id 5.5.5.5
+ passive interface lo
+ router-info area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..33473c9
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/rt5/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address ::ffff:0505:0505/128
+!
+interface eth-rt2
+ ip address 10.0.3.2/24
+!
+interface eth-rt4
+ ip address 10.0.5.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py b/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py
new file mode 100755
index 0000000..93a2339
--- /dev/null
+++ b/tests/topotests/bfd_ospf_topo1/test_bfd_ospf_topo1.py
@@ -0,0 +1,273 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_ospf_topo1.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_ospf_topo1.py:
+
+ +---------+
+ | |
+ eth-rt2 (.1) | RT1 | eth-rt3 (.1)
+ +----------+ 1.1.1.1 +----------+
+ | | | |
+ | +---------+ |
+ | |
+ | 10.0.2.0/24 |
+ | |
+ | eth-rt1 | (.2)
+ | 10.0.1.0/24 +----+----+
+ | | |
+ | | RT3 |
+ | | 3.3.3.3 |
+ | | |
+ (.2) | eth-rt1 +----+----+
+ +----+----+ eth-rt4 | (.1)
+ | | |
+ | RT2 | |
+ | 2.2.2.2 | 10.0.4.0/24 |
+ | | |
+ +----+----+ |
+ (.1) | eth-rt5 eth-rt3 | (.2)
+ | +----+----+
+ | | |
+ | | RT4 |
+ | | 4.4.4.4 |
+ | | |
+ | +----+----+
+ | 10.0.3.0/24 eth-rt5 | (.1)
+ | |
+ | |
+ | 10.0.5.0/24 |
+ | |
+ | +---------+ |
+ | | | |
+ +----------+ RT5 +----------+
+ eth-rt2 (.2) | 5.5.5.5 | eth-rt4 (.2)
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.ospfd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("rt1:eth-rt2", "rt2:eth-rt1"),
+ "s2": ("rt1:eth-rt3", "rt3:eth-rt1"),
+ "s3": ("rt2:eth-rt5", "rt5:eth-rt2"),
+ "s4": ("rt3:eth-rt4", "rt4:eth-rt3"),
+ "s5": ("rt4:eth-rt5", "rt5:eth-rt4"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ 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 print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def router_compare_json_output(rname, command, reference, count=40, wait=2):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=count, wait=wait)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+## TEST STEPS
+
+
+def test_rib_ospf_step1():
+ logger.info("Test (step 1): verify RIB for OSPF")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step1/show_ip_route.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step1/show_ipv6_route.ref"
+ )
+
+
+def test_bfd_ospf_sessions_step2():
+ logger.info("Test (step 2): verify BFD peers for OSPF")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # BFD is just used on three routers
+ for rt in ["rt1", "rt2", "rt3"]:
+ router_compare_json_output(
+ rt, "show bfd peers json", "step2/show_bfd_peers.ref"
+ )
+
+
+def test_bfd_ospf_interface_failure_rt2_step3():
+ logger.info("Test (step 3): Check failover handling with RT2 down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt2 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ topotest.sleep(2, "Wait for BFD down notification")
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt2_down.ref", 10, 2
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt2_down.ref", 10, 2
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt2_down.ref", 10, 2
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt2"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+def test_bfd_ospf_interface_failure_rt3_step3():
+ logger.info("Test (step 3): Check failover handling with RT3 down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Let's kill the interface on rt3 and see what happens with the RIB and BFD on rt1
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=False)
+
+ # By default BFD provides a recovery time of 900ms plus jitter, so let's wait
+ # initial 2 seconds to let the CI not suffer.
+ topotest.sleep(2, "Wait for BFD down notification")
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_rt3_down.ref", 10, 2
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_rt3_down.ref", 10, 2
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_rt3_down.ref", 10, 2
+ )
+
+ # Check recovery, this can take some time
+ tgen.gears["rt3"].link_enable("eth-rt1", enabled=True)
+
+ router_compare_json_output(
+ "rt1", "show ip route ospf json", "step3/show_ip_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show ipv6 route ospf json", "step3/show_ipv6_route_healthy.ref"
+ )
+ router_compare_json_output(
+ "rt1", "show bfd peers json", "step3/show_bfd_peers_healthy.ref"
+ )
+
+
+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/bfd_profiles_topo1/__init__.py b/tests/topotests/bfd_profiles_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/__init__.py
diff --git a/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json
new file mode 100644
index 0000000..86a7e51
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r1/bfd-peers-initial.json
@@ -0,0 +1,38 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r1-eth1",
+ "multihop": false,
+ "peer": "172.16.100.2",
+ "receive-interval": 800,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 300,
+ "remote-transmit-interval": 300,
+ "status": "up",
+ "transmit-interval": 800,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r1-eth0",
+ "multihop": false,
+ "peer": "172.16.0.1",
+ "receive-interval": 800,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 800,
+ "remote-transmit-interval": 800,
+ "status": "up",
+ "transmit-interval": 800,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf
new file mode 100644
index 0000000..c2ac9c1
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r1/bfdd.conf
@@ -0,0 +1,15 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ profile slowtx
+ receive-interval 800
+ transmit-interval 800
+ echo receive-interval 400
+ !
+ peer 172.16.0.1 interface r1-eth0
+ profile slowtx
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..373a0c5
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r1/ospfd.conf
@@ -0,0 +1,11 @@
+interface r1-eth1
+ ip ospf area 0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf bfd
+ ip ospf bfd profile slowtx
+!
+router ospf
+ ospf router-id 10.254.254.1
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r1/zebra.conf b/tests/topotests/bfd_profiles_topo1/r1/zebra.conf
new file mode 100644
index 0000000..4b7982b
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r1/zebra.conf
@@ -0,0 +1,9 @@
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 172.16.0.2/24
+!
+interface r1-eth1
+ ip address 172.16.100.1/24
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json
new file mode 100644
index 0000000..503f776
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r2/bfd-peers-initial.json
@@ -0,0 +1,40 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r2-eth0",
+ "multihop": false,
+ "peer": "172.16.0.2",
+ "receive-interval": 800,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 800,
+ "remote-transmit-interval": 800,
+ "remote-echo-receive-interval": 400,
+ "status": "up",
+ "transmit-interval": 800,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r2-eth1",
+ "multihop": false,
+ "peer": "172.16.1.1",
+ "receive-interval": 250,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 300,
+ "remote-transmit-interval": 300,
+ "status": "up",
+ "transmit-interval": 250,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf
new file mode 100644
index 0000000..b68eecb
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r2/bfdd.conf
@@ -0,0 +1,19 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ profile slowtx
+ receive-interval 800
+ transmit-interval 800
+ !
+ profile fasttx
+ receive-interval 250
+ transmit-interval 250
+ echo receive-interval disabled
+ !
+ peer 172.16.0.2 interface r2-eth0
+ profile slowtx
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf b/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..1aab1d1
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r2/bgpd.conf
@@ -0,0 +1,21 @@
+! debug bgp neighbor-events
+!
+router bgp 100
+ bgp router-id 10.254.254.2
+ no bgp ebgp-requires-policy
+ neighbor 172.16.1.1 remote-as 100
+ neighbor 172.16.1.1 timers 3 10
+ neighbor 172.16.1.1 bfd profile fasttx
+ neighbor 2001:db8:2::2 remote-as 200
+ neighbor 2001:db8:2::2 timers 3 10
+ neighbor 2001:db8:2::2 ebgp-multihop 2
+ neighbor 2001:db8:2::2 bfd profile slowtx
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 172.16.1.1 activate
+ neighbor 2001:db8:2::2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r2/zebra.conf b/tests/topotests/bfd_profiles_topo1/r2/zebra.conf
new file mode 100644
index 0000000..6acef13
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r2/zebra.conf
@@ -0,0 +1,10 @@
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 172.16.0.1/24
+!
+interface r2-eth1
+ ip address 172.16.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json
new file mode 100644
index 0000000..d987a0a
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r3/bfd-peers-initial.json
@@ -0,0 +1,40 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r3-eth0",
+ "multihop": false,
+ "peer": "172.16.1.2",
+ "receive-interval": 300,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
+ "remote-echo-receive-interval": 0,
+ "status": "up",
+ "transmit-interval": 300,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r3-eth1",
+ "local": "*",
+ "multihop": false,
+ "peer": "*",
+ "receive-interval": 250,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 300,
+ "remote-transmit-interval": 300,
+ "status": "up",
+ "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
new file mode 100644
index 0000000..f3a86ed
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r3/bfdd.conf
@@ -0,0 +1,10 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ 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
new file mode 100644
index 0000000..65647b3
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r3/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 100
+ bgp router-id 10.254.254.3
+ neighbor 172.16.1.2 remote-as 100
+ neighbor 172.16.1.2 timers 3 10
+ neighbor 172.16.1.2 bfd profile DOES_NOT_EXIST
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 172.16.1.2 activate
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r3/isisd.conf b/tests/topotests/bfd_profiles_topo1/r3/isisd.conf
new file mode 100644
index 0000000..3bba2b0
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r3/isisd.conf
@@ -0,0 +1,17 @@
+hostname r3
+!
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+!
+interface r3-eth1
+ ipv6 router isis lan
+ isis circuit-type level-1
+ isis bfd
+ isis bfd profile fasttx
+!
+router isis lan
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ redistribute ipv6 connected level-1
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r3/zebra.conf b/tests/topotests/bfd_profiles_topo1/r3/zebra.conf
new file mode 100644
index 0000000..2297bfa
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r3/zebra.conf
@@ -0,0 +1,12 @@
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 172.16.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
+interface r3-eth1
+ ipv6 address 2001:db8:2::1/64
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json
new file mode 100644
index 0000000..9ab7479
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/bfd-peers-initial.json
@@ -0,0 +1,41 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r4-eth0",
+ "local": "*",
+ "multihop": false,
+ "peer": "*",
+ "receive-interval": 300,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
+ "status": "up",
+ "transmit-interval": 300,
+ "uptime": "*",
+ "vrf": "default"
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r4-eth1",
+ "local": "*",
+ "multihop": false,
+ "peer": "*",
+ "receive-interval": 250,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-echo-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 300,
+ "remote-transmit-interval": 300,
+ "status": "up",
+ "transmit-interval": 250,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf
new file mode 100644
index 0000000..a5d1e25
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/bfdd.conf
@@ -0,0 +1,10 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ profile fast-tx
+ receive-interval 250
+ transmit-interval 250
+ !
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf b/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf
new file mode 100644
index 0000000..12d6827
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/bgpd.conf
@@ -0,0 +1,18 @@
+! debug bgp neighbor-events
+!
+router bgp 200
+ bgp router-id 10.254.254.4
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::2 remote-as 100
+ neighbor 2001:db8:1::2 timers 3 10
+ neighbor 2001:db8:1::2 ebgp-multihop 2
+ neighbor 2001:db8:1::2 bfd profile DOES_NOT_EXIST
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::2 activate
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r4/isisd.conf b/tests/topotests/bfd_profiles_topo1/r4/isisd.conf
new file mode 100644
index 0000000..18009ce
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/isisd.conf
@@ -0,0 +1,17 @@
+hostname r4
+!
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+!
+interface r4-eth0
+ ipv6 router isis lan
+ isis circuit-type level-1
+ isis bfd
+ isis bfd profile DOES_NOT_EXIST
+!
+router isis lan
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ redistribute ipv6 connected level-1
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf
new file mode 100644
index 0000000..4ef28c3
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/ospf6d.conf
@@ -0,0 +1,10 @@
+interface r4-eth1
+ ipv6 ospf6 bfd profile fast-tx
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ redistribute connected
+ interface r4-eth1 area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r4/zebra.conf b/tests/topotests/bfd_profiles_topo1/r4/zebra.conf
new file mode 100644
index 0000000..753041f
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r4/zebra.conf
@@ -0,0 +1,12 @@
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.4/32
+!
+interface r4-eth0
+ ipv6 address 2001:db8:2::2/64
+!
+interface r4-eth1
+ ip address 172.16.3.1/24
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json
new file mode 100644
index 0000000..0fe56d5
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r5/bfd-peers-initial.json
@@ -0,0 +1,21 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r5-eth0",
+ "local": "*",
+ "multihop": false,
+ "peer": "*",
+ "receive-interval": 300,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
+ "status": "up",
+ "transmit-interval": 300,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf
new file mode 100644
index 0000000..670fd44
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r5/bfdd.conf
@@ -0,0 +1,11 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ ! profile is commented out on purpose.
+ !profile fasttx
+ ! receive-interval 250
+ ! transmit-interval 250
+ !!
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf
new file mode 100644
index 0000000..20b53cf
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r5/ospf6d.conf
@@ -0,0 +1,10 @@
+interface r5-eth0
+ ipv6 ospf6 bfd
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.5
+ redistribute connected
+ interface r5-eth0 area 0.0.0.0
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r5/zebra.conf b/tests/topotests/bfd_profiles_topo1/r5/zebra.conf
new file mode 100644
index 0000000..de8ae16
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r5/zebra.conf
@@ -0,0 +1,6 @@
+interface lo
+ ip address 10.254.254.5/32
+!
+interface r5-eth0
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json b/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json
new file mode 100644
index 0000000..ec973eb
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r6/bfd-peers-initial.json
@@ -0,0 +1,20 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "id": "*",
+ "interface": "r6-eth0",
+ "multihop": false,
+ "peer": "172.16.100.1",
+ "receive-interval": 300,
+ "remote-detect-multiplier": 3,
+ "remote-diagnostic": "ok",
+ "remote-id": "*",
+ "remote-receive-interval": 800,
+ "remote-transmit-interval": 800,
+ "status": "up",
+ "transmit-interval": 300,
+ "uptime": "*",
+ "vrf": "default"
+ }
+]
diff --git a/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf b/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf
new file mode 100644
index 0000000..670fd44
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r6/bfdd.conf
@@ -0,0 +1,11 @@
+! debug bfd peer
+! debug bfd network
+! debug bfd zebra
+!
+bfd
+ ! profile is commented out on purpose.
+ !profile fasttx
+ ! receive-interval 250
+ ! transmit-interval 250
+ !!
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf b/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf
new file mode 100644
index 0000000..d8fce34
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r6/ospfd.conf
@@ -0,0 +1,10 @@
+interface r6-eth0
+ ip ospf area 0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 10.254.254.6
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_profiles_topo1/r6/zebra.conf b/tests/topotests/bfd_profiles_topo1/r6/zebra.conf
new file mode 100644
index 0000000..c0804b9
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/r6/zebra.conf
@@ -0,0 +1,6 @@
+interface lo
+ ip address 10.254.254.6/32
+!
+interface r6-eth0
+ ip address 172.16.100.2/24
+!
diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot
new file mode 100644
index 0000000..a393609
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.dot
@@ -0,0 +1,97 @@
+## 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-profiles-topo1";
+
+ # 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,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n172.16.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n172.16.1.0/24\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n2001:db8:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw4 [
+ shape=oval,
+ label="sw4\n2001:db8:3::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw5 [
+ shape=oval,
+ label="sw5\n172.16.100.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+ r2 -- sw2 [label="eth1"];
+ r3 -- sw2 [label="eth0"];
+
+ r3 -- sw3 [label="eth1"];
+ r4 -- sw3 [label="eth0"];
+
+ r4 -- sw4 [label="eth1"];
+ r5 -- sw4 [label="eth0"];
+
+ r1 -- sw5 [label="eth1"];
+ r6 -- sw5 [label="eth0"];
+}
diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png
new file mode 100644
index 0000000..775fae1
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.png
Binary files differ
diff --git a/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py
new file mode 100644
index 0000000..169f90a
--- /dev/null
+++ b/tests/topotests/bfd_profiles_topo1/test_bfd_profiles_topo1.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_profiles_topo1.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_profiles_topo1.py: Test the FRR BFD profile protocol integration.
+"""
+
+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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.ospfd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r2", "r3"),
+ "s3": ("r3", "r4"),
+ "s4": ("r4", "r5"),
+ "s5": ("r1", "r6"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/bfdd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BFD, daemon_file)
+
+ daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BGP, daemon_file)
+
+ daemon_file = "{}/{}/isisd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ISIS, daemon_file)
+
+ daemon_file = "{}/{}/ospfd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF, daemon_file)
+
+ daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF6, daemon_file)
+
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_wait_protocols_convergence():
+ "Wait for all protocols 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 <-> R6 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.6/32", "ospf")
+
+ # Wait for R6 <-> R1 convergence.
+ expect_loopback_route("r6", "ip", "10.254.254.1/32", "ospf")
+
+ # Wait for R2 <-> R3 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.3/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", "ipv6", "2001:db8:3::/64", "isis")
+
+ # Wait for R4 <-> R3 convergence.
+ expect_loopback_route("r4", "ipv6", "2001:db8:1::/64", "isis")
+
+ # Wait for R4 <-> R5 convergence.
+ expect_loopback_route("r4", "ipv6", "2001:db8:3::/64", "ospf6")
+
+ # Wait for R5 <-> R4 convergence.
+ expect_loopback_route("r5", "ipv6", "2001:db8:2::/64", "ospf6")
+
+
+def test_bfd_profile_values():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bfd peers to go up and checking profile values")
+
+ for router in tgen.routers().values():
+ json_file = "{}/{}/bfd-peers-initial.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=12, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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/bfd_topo1/__init__.py b/tests/topotests/bfd_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_topo1/__init__.py
diff --git a/tests/topotests/bfd_topo1/r1/bfdd.conf b/tests/topotests/bfd_topo1/r1/bfdd.conf
new file mode 100644
index 0000000..b9efbaf
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/bfdd.conf
@@ -0,0 +1,11 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.2
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo1/r1/bgp_prefixes.json b/tests/topotests/bfd_topo1/r1/bgp_prefixes.json
new file mode 100644
index 0000000..1262f5e
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r1/bgp_summary.json b/tests/topotests/bfd_topo1/r1/bgp_summary.json
new file mode 100644
index 0000000..fa07d60
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 101,
+ "peers": {
+ "192.168.0.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r1/bgpd.conf b/tests/topotests/bfd_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..57bde1f
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 101
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.0.2 remote-as 102
+ neighbor 192.168.0.2 timers 3 10
+ neighbor 192.168.0.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo1/r1/peers.json b/tests/topotests/bfd_topo1/r1/peers.json
new file mode 100644
index 0000000..f49768f
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/peers.json
@@ -0,0 +1,8 @@
+[
+ {
+ "remote-receive-interval": 1000,
+ "remote-transmit-interval": 500,
+ "peer": "192.168.0.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_topo1/r1/zebra.conf b/tests/topotests/bfd_topo1/r1/zebra.conf
new file mode 100644
index 0000000..a14cd7a
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r1/zebra.conf
@@ -0,0 +1,3 @@
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bfd_topo1/r2/bfdd.conf b/tests/topotests/bfd_topo1/r2/bfdd.conf
new file mode 100644
index 0000000..0d1e17e
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/bfdd.conf
@@ -0,0 +1,17 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.1
+ receive-interval 1000
+ transmit-interval 500
+ echo-mode
+ no shutdown
+ !
+ peer 192.168.1.1
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo1/r2/bgp_prefixes.json b/tests/topotests/bfd_topo1/r2/bgp_prefixes.json
new file mode 100644
index 0000000..0d47c0f
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.0.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.1.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.2.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r2/bgp_summary.json b/tests/topotests/bfd_topo1/r2/bgp_summary.json
new file mode 100644
index 0000000..c0ef11a
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/bgp_summary.json
@@ -0,0 +1,19 @@
+{
+ "ipv4Unicast": {
+ "as": 102,
+ "peers": {
+ "192.168.0.1": {
+ "remoteAs": 101,
+ "state": "Established"
+ },
+ "192.168.1.1": {
+ "remoteAs": 103,
+ "state": "Established"
+ },
+ "192.168.2.1": {
+ "remoteAs": 104,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r2/bgpd.conf b/tests/topotests/bfd_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..50d75ab
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/bgpd.conf
@@ -0,0 +1,16 @@
+router bgp 102
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.0.1 remote-as 101
+ neighbor 192.168.0.1 timers 3 10
+ neighbor 192.168.0.1 bfd
+ neighbor 192.168.1.1 remote-as 103
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.1.1 bfd
+ neighbor 192.168.2.1 remote-as 104
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.2.1 bfd
+ address-family ipv4 unicast
+ network 10.254.254.2/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo1/r2/peers.json b/tests/topotests/bfd_topo1/r2/peers.json
new file mode 100644
index 0000000..267459c
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "peer": "192.168.0.1",
+ "status": "up"
+ },
+ {
+ "remote-echo-receive-interval": 100,
+ "peer": "192.168.1.1",
+ "status": "up"
+ },
+ {
+ "remote-transmit-interval": 2000,
+ "remote-receive-interval": 2000,
+ "peer": "192.168.2.1",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_topo1/r2/zebra.conf b/tests/topotests/bfd_topo1/r2/zebra.conf
new file mode 100644
index 0000000..568abe7
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r2/zebra.conf
@@ -0,0 +1,9 @@
+interface r2-eth0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1
+ ip address 192.168.1.2/24
+!
+interface r2-eth2
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bfd_topo1/r3/bfdd.conf b/tests/topotests/bfd_topo1/r3/bfdd.conf
new file mode 100644
index 0000000..e091a1c
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/bfdd.conf
@@ -0,0 +1,12 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.1.2
+ echo-interval 100
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo1/r3/bgp_prefixes.json b/tests/topotests/bfd_topo1/r3/bgp_prefixes.json
new file mode 100644
index 0000000..36fca17
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r3/bgp_summary.json b/tests/topotests/bfd_topo1/r3/bgp_summary.json
new file mode 100644
index 0000000..d478333
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 103,
+ "peers": {
+ "192.168.1.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r3/bgpd.conf b/tests/topotests/bfd_topo1/r3/bgpd.conf
new file mode 100644
index 0000000..ce6055d
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 103
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as 102
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 192.168.1.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.3/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo1/r3/peers.json b/tests/topotests/bfd_topo1/r3/peers.json
new file mode 100644
index 0000000..ef38008
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.1.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_topo1/r3/zebra.conf b/tests/topotests/bfd_topo1/r3/zebra.conf
new file mode 100644
index 0000000..b4fd80f
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r3/zebra.conf
@@ -0,0 +1,3 @@
+interface r3-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bfd_topo1/r4/bfdd.conf b/tests/topotests/bfd_topo1/r4/bfdd.conf
new file mode 100644
index 0000000..63dd738
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/bfdd.conf
@@ -0,0 +1,12 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.2.2
+ transmit-interval 2000
+ receive-interval 2000
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo1/r4/bgp_prefixes.json b/tests/topotests/bfd_topo1/r4/bgp_prefixes.json
new file mode 100644
index 0000000..efe7d47
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r4/bgp_summary.json b/tests/topotests/bfd_topo1/r4/bgp_summary.json
new file mode 100644
index 0000000..7d81784
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 104,
+ "peers": {
+ "192.168.2.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_topo1/r4/bgpd.conf b/tests/topotests/bfd_topo1/r4/bgpd.conf
new file mode 100644
index 0000000..0d032b4
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 104
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.2.2 remote-as 102
+ neighbor 192.168.2.2 timers 3 10
+ neighbor 192.168.2.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.4/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo1/r4/peers.json b/tests/topotests/bfd_topo1/r4/peers.json
new file mode 100644
index 0000000..3714008
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.2.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_topo1/r4/zebra.conf b/tests/topotests/bfd_topo1/r4/zebra.conf
new file mode 100644
index 0000000..afdd44b
--- /dev/null
+++ b/tests/topotests/bfd_topo1/r4/zebra.conf
@@ -0,0 +1,3 @@
+interface r4-eth0
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.dot b/tests/topotests/bfd_topo1/test_bfd_topo1.dot
new file mode 100644
index 0000000..c84ace2
--- /dev/null
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.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-topo1";
+
+ # 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.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n192.168.2.0/24",
+ 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"];
+ r2 -- sw3 [label="eth2\n.2"];
+}
diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.jpg b/tests/topotests/bfd_topo1/test_bfd_topo1.jpg
new file mode 100644
index 0000000..4d6d56e
--- /dev/null
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.jpg
Binary files differ
diff --git a/tests/topotests/bfd_topo1/test_bfd_topo1.py b/tests/topotests/bfd_topo1/test_bfd_topo1.py
new file mode 100644
index 0000000..c9020f1
--- /dev/null
+++ b/tests/topotests/bfd_topo1/test_bfd_topo1.py
@@ -0,0 +1,226 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 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_topo1.py: Test the FRR BFD daemon.
+"""
+
+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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r2", "r3"),
+ "s3": ("r2", "r4"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # Verify that we are using the proper version and that the BFD
+ # daemon exists.
+ for router in router_list.values():
+ # Check for Version
+ if router.has_version("<", "5.1"):
+ tgen.set_error("Unsupported FRR version")
+ break
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bfd peers to go up")
+
+ for router in tgen.routers().values():
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bgp_convergence():
+ "Assert that BGP is converging."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers to go up")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_convergence():
+ "Assert that BGP is converging before setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers converge")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bfd_fast_convergence():
+ """
+ Assert that BFD notices the link down after simulating network
+ failure.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Disable r1-eth0 link.
+ tgen.gears["r1"].link_enable("r1-eth0", enabled=False)
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info("waiting for BFD converge")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ # Load the same file as previous test, but expect R1 to be down.
+ if router.name == "r1":
+ for peer in expected:
+ if peer["peer"] == "192.168.0.2":
+ peer["status"] = "down"
+ else:
+ for peer in expected:
+ if peer["peer"] == "192.168.0.1":
+ peer["status"] = "down"
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_reconvergence():
+ "Assert that BGP is converging after setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for BGP re convergence")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+
+ # Load the same file as previous test, but set networks to None
+ # to test absence.
+ if router.name == "r1":
+ expected["routes"]["10.254.254.2/32"] = None
+ expected["routes"]["10.254.254.3/32"] = None
+ expected["routes"]["10.254.254.4/32"] = None
+ else:
+ expected["routes"]["10.254.254.1/32"] = None
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+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/bfd_topo2/__init__.py b/tests/topotests/bfd_topo2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_topo2/__init__.py
diff --git a/tests/topotests/bfd_topo2/r1/bfdd.conf b/tests/topotests/bfd_topo2/r1/bfdd.conf
new file mode 100644
index 0000000..df8baeb
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/bfdd.conf
@@ -0,0 +1,10 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 2001:db8:4::1 multihop local-address 2001:db8:1::1
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo2/r1/bgpd.conf b/tests/topotests/bfd_topo2/r1/bgpd.conf
new file mode 100644
index 0000000..0918796
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 101
+ bgp router-id 10.254.254.1
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r1-eth0 interface peer-group r2g
+ neighbor r1-eth0 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo2/r1/ipv4_routes.json b/tests/topotests/bfd_topo2/r1/ipv4_routes.json
new file mode 100644
index 0000000..650c0a8
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/ipv4_routes.json
@@ -0,0 +1,59 @@
+{
+ "10.0.3.0/24": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.0.3.0/24",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.2/32",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.1/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r1/ipv6_routes.json b/tests/topotests/bfd_topo2/r1/ipv6_routes.json
new file mode 100644
index 0000000..50c1f9a
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/ipv6_routes.json
@@ -0,0 +1,53 @@
+{
+ "2001:db8:4::/64": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "2001:db8:1::/64": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r1/peers.json b/tests/topotests/bfd_topo2/r1/peers.json
new file mode 100644
index 0000000..9bce991
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/peers.json
@@ -0,0 +1,31 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:4::1",
+ "local":"2001:db8:1::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ },
+ {
+ "multihop":false,
+ "interface":"r1-eth0",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_topo2/r1/zebra.conf b/tests/topotests/bfd_topo2/r1/zebra.conf
new file mode 100644
index 0000000..7fe5eb2
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r1/zebra.conf
@@ -0,0 +1,6 @@
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bfd_topo2/r2/bfdd.conf b/tests/topotests/bfd_topo2/r2/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_topo2/r2/bgpd.conf b/tests/topotests/bfd_topo2/r2/bgpd.conf
new file mode 100644
index 0000000..55d4856
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 102
+ bgp router-id 10.254.254.2
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r2-eth0 interface peer-group r2g
+ neighbor r2-eth0 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_topo2/r2/ipv4_routes.json b/tests/topotests/bfd_topo2/r2/ipv4_routes.json
new file mode 100644
index 0000000..3d49b17
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/ipv4_routes.json
@@ -0,0 +1,92 @@
+{
+ "10.0.3.0/24": [
+ {
+ "distance": 110,
+ "protocol": "ospf",
+ "metric": 10,
+ "prefix": "10.0.3.0/24",
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "interfaceName": "r2-eth1"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.0.3.0/24",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r2-eth1",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "distance": 110,
+ "protocol": "ospf",
+ "metric": 20,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.3/32",
+ "nexthops": [
+ {
+ "interfaceName": "r2-eth1",
+ "ip": "10.0.3.1",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.2/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.1/32",
+ "nexthops": [
+ {
+ "interfaceName": "r2-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r2/ipv6_routes.json b/tests/topotests/bfd_topo2/r2/ipv6_routes.json
new file mode 100644
index 0000000..4f3c74c
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/ipv6_routes.json
@@ -0,0 +1,53 @@
+{
+ "2001:db8:4::/64": [
+ {
+ "distance": 110,
+ "protocol": "ospf6",
+ "metric": 10,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "interfaceName": "r2-eth2"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r2-eth2",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1::/64": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r2-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r2/ospf6d.conf b/tests/topotests/bfd_topo2/r2/ospf6d.conf
new file mode 100644
index 0000000..524e2c9
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/ospf6d.conf
@@ -0,0 +1,11 @@
+interface r2-eth2
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 bfd
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.2
+ redistribute connected
+ redistribute bgp
+!
diff --git a/tests/topotests/bfd_topo2/r2/ospfd.conf b/tests/topotests/bfd_topo2/r2/ospfd.conf
new file mode 100644
index 0000000..c786f1f
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/ospfd.conf
@@ -0,0 +1,11 @@
+interface r2-eth1
+ ip ospf area 0.0.0.1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 10.254.254.2
+ redistribute connected
+ redistribute bgp
+!
diff --git a/tests/topotests/bfd_topo2/r2/peers.json b/tests/topotests/bfd_topo2/r2/peers.json
new file mode 100644
index 0000000..ec2135c
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/peers.json
@@ -0,0 +1,45 @@
+[
+ {
+ "status": "up",
+ "transmit-interval": 300,
+ "remote-receive-interval": 300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "diagnostic": "ok",
+ "multihop": false,
+ "interface": "r2-eth0",
+ "remote-transmit-interval": 300,
+ "receive-interval": 300,
+ "remote-echo-receive-interval": 50,
+ "remote-diagnostic": "ok"
+ },
+ {
+ "status": "up",
+ "transmit-interval": 300,
+ "remote-receive-interval": 300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "diagnostic": "ok",
+ "multihop": false,
+ "interface": "r2-eth2",
+ "remote-transmit-interval": 300,
+ "receive-interval": 300,
+ "remote-echo-receive-interval": 50,
+ "remote-diagnostic": "ok"
+ },
+ {
+ "status": "up",
+ "transmit-interval": 300,
+ "remote-receive-interval": 300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "diagnostic": "ok",
+ "multihop": false,
+ "interface": "r2-eth1",
+ "remote-transmit-interval": 300,
+ "receive-interval": 300,
+ "remote-echo-receive-interval": 50,
+ "remote-diagnostic": "ok",
+ "peer": "10.0.3.1"
+ }
+]
diff --git a/tests/topotests/bfd_topo2/r2/zebra.conf b/tests/topotests/bfd_topo2/r2/zebra.conf
new file mode 100644
index 0000000..cccbf65
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r2/zebra.conf
@@ -0,0 +1,15 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ipv6 address 2001:db8:1::2/64
+!
+interface r2-eth1
+ ip address 10.0.3.2/24
+!
+interface r2-eth2
+ ipv6 address 2001:db8:4::2/64
+!
diff --git a/tests/topotests/bfd_topo2/r3/bfdd.conf b/tests/topotests/bfd_topo2/r3/bfdd.conf
new file mode 100644
index 0000000..ee7144d
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/bfdd.conf
@@ -0,0 +1,5 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
diff --git a/tests/topotests/bfd_topo2/r3/ipv4_routes.json b/tests/topotests/bfd_topo2/r3/ipv4_routes.json
new file mode 100644
index 0000000..e96fdc0
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/ipv4_routes.json
@@ -0,0 +1,93 @@
+{
+ "10.0.3.0/24": [
+ {
+ "distance": 110,
+ "protocol": "ospf",
+ "metric": 10,
+ "prefix": "10.0.3.0/24",
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "interfaceName": "r3-eth0"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.0.3.0/24",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r3-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.3/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "distance": 110,
+ "protocol": "ospf",
+ "metric": 20,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.2/32",
+ "nexthops": [
+ {
+ "interfaceName": "r3-eth0",
+ "ip": "10.0.3.2",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "distance": 110,
+ "protocol": "ospf",
+ "metric": 20,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.1/32",
+ "nexthops": [
+ {
+ "interfaceName": "r3-eth0",
+ "ip": "10.0.3.2",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r3/ipv6_routes.json b/tests/topotests/bfd_topo2/r3/ipv6_routes.json
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/ipv6_routes.json
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/bfd_topo2/r3/ospfd.conf b/tests/topotests/bfd_topo2/r3/ospfd.conf
new file mode 100644
index 0000000..932ab4d
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/ospfd.conf
@@ -0,0 +1,10 @@
+interface r3-eth0
+ ip ospf area 0.0.0.1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf bfd
+!
+router ospf
+ ospf router-id 10.254.254.3
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_topo2/r3/peers.json b/tests/topotests/bfd_topo2/r3/peers.json
new file mode 100644
index 0000000..c19c980
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "status": "up",
+ "transmit-interval": 300,
+ "remote-receive-interval": 300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval": 0,
+ "diagnostic": "ok",
+ "multihop": false,
+ "interface": "r3-eth0",
+ "remote-transmit-interval": 300,
+ "receive-interval": 300,
+ "remote-echo-receive-interval": 50,
+ "remote-diagnostic": "ok",
+ "peer": "10.0.3.2"
+ }
+]
diff --git a/tests/topotests/bfd_topo2/r3/zebra.conf b/tests/topotests/bfd_topo2/r3/zebra.conf
new file mode 100644
index 0000000..96fd08c
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r3/zebra.conf
@@ -0,0 +1,6 @@
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 10.0.3.1/24
+!
diff --git a/tests/topotests/bfd_topo2/r4/bfdd.conf b/tests/topotests/bfd_topo2/r4/bfdd.conf
new file mode 100644
index 0000000..c1e8d28
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/bfdd.conf
@@ -0,0 +1,10 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 2001:db8:1::1 multihop local-address 2001:db8:4::1
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_topo2/r4/ipv4_routes.json b/tests/topotests/bfd_topo2/r4/ipv4_routes.json
new file mode 100644
index 0000000..dc394aa
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/ipv4_routes.json
@@ -0,0 +1,21 @@
+{
+ "10.254.254.4/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "10.254.254.4/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r4/ipv6_routes.json b/tests/topotests/bfd_topo2/r4/ipv6_routes.json
new file mode 100644
index 0000000..eb571d5
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/ipv6_routes.json
@@ -0,0 +1,53 @@
+{
+ "2001:db8:4::/64": [
+ {
+ "distance": 110,
+ "protocol": "ospf6",
+ "metric": 10,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "interfaceName": "r4-eth0"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:4::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r4-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1::/64": [
+ {
+ "distance": 110,
+ "protocol": "ospf6",
+ "metric": 20,
+ "selected": true,
+ "installed": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "interfaceName": "r4-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bfd_topo2/r4/ospf6d.conf b/tests/topotests/bfd_topo2/r4/ospf6d.conf
new file mode 100644
index 0000000..2f38c51
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/ospf6d.conf
@@ -0,0 +1,10 @@
+interface r4-eth0
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 bfd
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ redistribute connected
+!
diff --git a/tests/topotests/bfd_topo2/r4/peers.json b/tests/topotests/bfd_topo2/r4/peers.json
new file mode 100644
index 0000000..dd26b9b
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/peers.json
@@ -0,0 +1,31 @@
+[
+ {
+ "multihop":true,
+ "peer":"2001:db8:1::1",
+ "local":"2001:db8:4::1",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ },
+ {
+ "multihop":false,
+ "interface":"r4-eth0",
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":300,
+ "transmit-interval":300,
+ "echo-receive-interval": 50,
+ "echo-transmit-interval":0,
+ "remote-receive-interval":300,
+ "remote-transmit-interval":300,
+ "remote-echo-receive-interval":50
+ }
+]
diff --git a/tests/topotests/bfd_topo2/r4/zebra.conf b/tests/topotests/bfd_topo2/r4/zebra.conf
new file mode 100644
index 0000000..e4f8fd8
--- /dev/null
+++ b/tests/topotests/bfd_topo2/r4/zebra.conf
@@ -0,0 +1,6 @@
+interface lo
+ ip address 10.254.254.4/32
+!
+interface r4-eth0
+ ipv6 address 2001:db8:4::1/64
+!
diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.dot b/tests/topotests/bfd_topo2/test_bfd_topo2.dot
new file mode 100644
index 0000000..6b68fb3
--- /dev/null
+++ b/tests/topotests/bfd_topo2/test_bfd_topo2.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-topo2";
+
+ # 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\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n2001:db8:4::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+ r2 -- sw2 [label="eth1"];
+ r3 -- sw2 [label="eth0"];
+
+ r2 -- sw3 [label="eth2"];
+ r4 -- sw3 [label="eth0"];
+}
diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.jpg b/tests/topotests/bfd_topo2/test_bfd_topo2.jpg
new file mode 100644
index 0000000..35fe562
--- /dev/null
+++ b/tests/topotests/bfd_topo2/test_bfd_topo2.jpg
Binary files differ
diff --git a/tests/topotests/bfd_topo2/test_bfd_topo2.py b/tests/topotests/bfd_topo2/test_bfd_topo2.py
new file mode 100644
index 0000000..a9b9358
--- /dev/null
+++ b/tests/topotests/bfd_topo2/test_bfd_topo2.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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_topo2.py: Test the FRR BFD daemon with multihop and BGP
+unnumbered.
+"""
+
+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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r2", "r3"),
+ "s3": ("r2", "r4"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/bfdd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BFD, daemon_file)
+
+ daemon_file = "{}/{}/bgpd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BGP, daemon_file)
+
+ daemon_file = "{}/{}/ospfd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF, daemon_file)
+
+ daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF6, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged before checking for the BFD
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv4 routing tables.
+ logger.info("Checking IPv4 routes for convergence")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check IPv6 routing tables.
+ logger.info("Checking IPv6 routes for convergence")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ipv6 route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bfd peers to go up")
+
+ for router in tgen.routers().values():
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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/bfd_topo3/__init__.py b/tests/topotests/bfd_topo3/__init__.py
new file mode 100644
index 0000000..e69de29
--- /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 0000000..3ce8d97
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r1/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 600
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r1/bfdd.conf b/tests/topotests/bfd_topo3/r1/bfdd.conf
new file mode 100644
index 0000000..60f129b
--- /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 0000000..4c75d66
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r1/bgpd.conf
@@ -0,0 +1,23 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::2 remote-as internal
+ neighbor 2001:db8:1::2 timers 3 10
+ neighbor 2001:db8:1::2 bfd profile fast-tx
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+ 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 timers 3 10
+ 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 0000000..64aee48
--- /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 0000000..a40f7e4
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r2/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 600,
+ "remote-transmit-interval": 600,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 600
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r2/bfdd.conf b/tests/topotests/bfd_topo3/r2/bfdd.conf
new file mode 100644
index 0000000..8297043
--- /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 0000000..7522576
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r2/bgpd.conf
@@ -0,0 +1,17 @@
+router bgp 100
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::1 remote-as internal
+ neighbor 2001:db8:1::1 timers 3 10
+ neighbor 2001:db8:1::1 bfd profile fast-tx
+ neighbor 2001:db8:2::1 remote-as external
+ neighbor 2001:db8:2::1 timers 3 10
+ 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 0000000..c7e22d4
--- /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 0000000..2182b26
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bfd-peers.json
@@ -0,0 +1,68 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r3/bfdd.conf b/tests/topotests/bfd_topo3/r3/bfdd.conf
new file mode 100644
index 0000000..51ce2ac
--- /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 0000000..82adf8b
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r3/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 300
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ 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 timers 3 10
+ neighbor 2001:db8:2::2 bfd profile slow-tx
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 timers 3 10
+ 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 0000000..14248fb
--- /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 0000000..2f41f25
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/bfd-peers.json
@@ -0,0 +1,46 @@
+[
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ },
+ {
+ "detect-multiplier": 3,
+ "diagnostic": "ok",
+ "echo-receive-interval": 50,
+ "echo-transmit-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-receive-interval": 50,
+ "remote-id": "*",
+ "remote-receive-interval": 2000,
+ "remote-transmit-interval": 2000,
+ "status": "up",
+ "uptime": "*",
+ "transmit-interval": 2000
+ }
+]
diff --git a/tests/topotests/bfd_topo3/r4/bfdd.conf b/tests/topotests/bfd_topo3/r4/bfdd.conf
new file mode 100644
index 0000000..e5fc164
--- /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 0000000..0aab6e3
--- /dev/null
+++ b/tests/topotests/bfd_topo3/r4/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 400
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:3::2 remote-as external
+ neighbor 2001:db8:3::2 timers 3 10
+ neighbor 2001:db8:3::2 bfd profile slow-tx
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 timers 3 10
+ 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 0000000..bf0cfcf
--- /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 0000000..502cea1
--- /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 0000000..6b53256
--- /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 0000000..978593e
--- /dev/null
+++ b/tests/topotests/bfd_topo3/test_bfd_topo3.py
@@ -0,0 +1,171 @@
+#!/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
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {
+ "s1": ("r1", "r2"),
+ "s2": ("r2", "r3"),
+ "s3": ("r3", "r4"),
+ }
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ 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/bfd_vrf_topo1/__init__.py b/tests/topotests/bfd_vrf_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/__init__.py
diff --git a/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf
new file mode 100644
index 0000000..0476df7
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/bfdd.conf
@@ -0,0 +1,15 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.2 vrf r1-bfd-cust1
+ receive-interval 1000
+ transmit-interval 1000
+ echo-mode
+ echo transmit-interval 1000
+ echo receive-interval 1000
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json
new file mode 100644
index 0000000..1262f5e
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.0.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json
new file mode 100644
index 0000000..fa07d60
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 101,
+ "peers": {
+ "192.168.0.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..cf72f30
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 101 vrf r1-bfd-cust1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.0.2 remote-as 102
+ neighbor 192.168.0.2 timers 3 10
+! neighbor 192.168.0.2 ebgp-multihop 10
+ neighbor 192.168.0.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r1/peers.json b/tests/topotests/bfd_vrf_topo1/r1/peers.json
new file mode 100644
index 0000000..57cea71
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/peers.json
@@ -0,0 +1,8 @@
+[
+ {
+ "remote-receive-interval": 1000,
+ "remote-transmit-interval": 1000,
+ "peer": "192.168.0.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_vrf_topo1/r1/zebra.conf b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf
new file mode 100644
index 0000000..62ed36f
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r1/zebra.conf
@@ -0,0 +1,3 @@
+interface r1-eth0 vrf r1-bfd-cust1
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf
new file mode 100644
index 0000000..69edd15
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/bfdd.conf
@@ -0,0 +1,23 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.1 vrf r2-bfd-cust1
+ receive-interval 1000
+ transmit-interval 1000
+ echo-mode
+ echo transmit-interval 1000
+ echo receive-interval 1000
+ no shutdown
+ !
+ peer 192.168.1.1 vrf r2-bfd-cust1
+ receive-interval 1000
+ transmit-interval 1000
+ echo-mode
+ echo transmit-interval 1000
+ echo receive-interval 1000
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json
new file mode 100644
index 0000000..0d47c0f
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.0.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.1.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.2.1",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.1",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json
new file mode 100644
index 0000000..c0ef11a
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/bgp_summary.json
@@ -0,0 +1,19 @@
+{
+ "ipv4Unicast": {
+ "as": 102,
+ "peers": {
+ "192.168.0.1": {
+ "remoteAs": 101,
+ "state": "Established"
+ },
+ "192.168.1.1": {
+ "remoteAs": 103,
+ "state": "Established"
+ },
+ "192.168.2.1": {
+ "remoteAs": 104,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..132011c
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/bgpd.conf
@@ -0,0 +1,16 @@
+router bgp 102 vrf r2-bfd-cust1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.0.1 remote-as 101
+ neighbor 192.168.0.1 timers 3 10
+ neighbor 192.168.0.1 bfd
+ neighbor 192.168.1.1 remote-as 103
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.1.1 bfd
+ neighbor 192.168.2.1 remote-as 104
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.2.1 bfd
+ address-family ipv4 unicast
+ network 10.254.254.2/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r2/peers.json b/tests/topotests/bfd_vrf_topo1/r2/peers.json
new file mode 100644
index 0000000..0a1c342
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/peers.json
@@ -0,0 +1,17 @@
+[
+ {
+ "peer": "192.168.0.1",
+ "status": "up"
+ },
+ {
+ "remote-echo-receive-interval": 1000,
+ "peer": "192.168.1.1",
+ "status": "up"
+ },
+ {
+ "remote-transmit-interval": 2000,
+ "remote-receive-interval": 2000,
+ "peer": "192.168.2.1",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_vrf_topo1/r2/zebra.conf b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf
new file mode 100644
index 0000000..1e817b1
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r2/zebra.conf
@@ -0,0 +1,9 @@
+interface r2-eth0 vrf r2-bfd-cust1
+ ip address 192.168.0.2/24
+!
+interface r2-eth1 vrf r2-bfd-cust1
+ ip address 192.168.1.2/24
+!
+interface r2-eth2 vrf r2-bfd-cust1
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf
new file mode 100644
index 0000000..00162b5
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/bfdd.conf
@@ -0,0 +1,14 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.1.2 vrf r3-bfd-cust1
+ receive-interval 1000
+ transmit-interval 1000
+ echo-interval 1000
+ echo-mode
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json
new file mode 100644
index 0000000..36fca17
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.4/32": [
+ {
+ "path": "102 104",
+ "prefix": "10.254.254.4",
+ "valid": true,
+ "peerId": "192.168.1.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.1.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json
new file mode 100644
index 0000000..d478333
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 103,
+ "peers": {
+ "192.168.1.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf
new file mode 100644
index 0000000..f764647
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 103 vrf r3-bfd-cust1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as 102
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 192.168.1.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.3/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r3/peers.json b/tests/topotests/bfd_vrf_topo1/r3/peers.json
new file mode 100644
index 0000000..ef38008
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.1.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_vrf_topo1/r3/zebra.conf b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf
new file mode 100644
index 0000000..e673459
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r3/zebra.conf
@@ -0,0 +1,3 @@
+interface r3-eth0 vrf r3-bfd-cust1
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf
new file mode 100644
index 0000000..119e5e5
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/bfdd.conf
@@ -0,0 +1,12 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.2.2 vrf r4-bfd-cust1
+ transmit-interval 2000
+ receive-interval 2000
+ no shutdown
+ !
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json b/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json
new file mode 100644
index 0000000..efe7d47
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/bgp_prefixes.json
@@ -0,0 +1,52 @@
+{
+ "routes": {
+ "10.254.254.1/32": [
+ {
+ "path": "102 101",
+ "prefix": "10.254.254.1",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.2/32": [
+ {
+ "path": "102",
+ "prefix": "10.254.254.2",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ],
+ "10.254.254.3/32": [
+ {
+ "path": "102 103",
+ "prefix": "10.254.254.3",
+ "valid": true,
+ "peerId": "192.168.2.2",
+ "prefixLen": 32,
+ "nexthops": [
+ {
+ "ip": "192.168.2.2",
+ "used": true,
+ "afi": "ipv4"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json b/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json
new file mode 100644
index 0000000..7d81784
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/bgp_summary.json
@@ -0,0 +1,11 @@
+{
+ "ipv4Unicast": {
+ "as": 104,
+ "peers": {
+ "192.168.2.2": {
+ "remoteAs": 102,
+ "state": "Established"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf
new file mode 100644
index 0000000..03353e4
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 104 vrf r4-bfd-cust1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.2.2 remote-as 102
+ neighbor 192.168.2.2 timers 3 10
+ neighbor 192.168.2.2 bfd
+ address-family ipv4 unicast
+ network 10.254.254.4/32
+ exit-address-family
+!
diff --git a/tests/topotests/bfd_vrf_topo1/r4/peers.json b/tests/topotests/bfd_vrf_topo1/r4/peers.json
new file mode 100644
index 0000000..3714008
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/peers.json
@@ -0,0 +1,6 @@
+[
+ {
+ "peer": "192.168.2.2",
+ "status": "up"
+ }
+]
diff --git a/tests/topotests/bfd_vrf_topo1/r4/zebra.conf b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf
new file mode 100644
index 0000000..15d3ec1
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/r4/zebra.conf
@@ -0,0 +1,3 @@
+interface r4-eth0 vrf r4-bfd-cust1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.dot
new file mode 100644
index 0000000..c84ace2
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.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-topo1";
+
+ # 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.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n192.168.2.0/24",
+ 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"];
+ r2 -- sw3 [label="eth2\n.2"];
+}
diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg
new file mode 100644
index 0000000..4d6d56e
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.jpg
Binary files differ
diff --git a/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py
new file mode 100644
index 0000000..acb86ea
--- /dev/null
+++ b/tests/topotests/bfd_vrf_topo1/test_bfd_vrf_topo1.py
@@ -0,0 +1,280 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_vrf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+# Copyright (c) 2019 by 6WIND
+#
+# 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_vrf_topo1.py: Test the FRR BFD daemon.
+"""
+
+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.
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ # 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # check for zebra capability
+ for rname, router in router_list.items():
+ if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False:
+ return pytest.skip(
+ "Skipping BFD Topo1 VRF NETNS feature. VRF NETNS backend not available on FRR"
+ )
+
+ if os.system("ip netns list") != 0:
+ return pytest.skip(
+ "Skipping BFD Topo1 VRF NETNS Test. NETNS not available on System"
+ )
+
+ logger.info("Testing with VRF Namespace support")
+
+ for rname, router in router_list.items():
+ # create VRF rx-bfd-cust1 and link rx-eth0 to rx-bfd-cust1
+ ns = "{}-bfd-cust1".format(rname)
+ router.net.add_netns(ns)
+ router.net.set_intf_netns(rname + "-eth0", ns, up=True)
+ if rname == "r2":
+ router.net.set_intf_netns(rname + "-eth1", ns, up=True)
+ router.net.set_intf_netns(rname + "-eth2", ns, up=True)
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ "--vrfwnetns",
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # Move interfaces out of vrf namespace and delete the namespace
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ if rname == "r2":
+ router.net.reset_intf_netns(rname + "-eth2")
+ router.net.reset_intf_netns(rname + "-eth1")
+ router.net.reset_intf_netns(rname + "-eth0")
+ router.net.delete_netns("{}-bfd-cust1".format(rname))
+ tgen.stop_topology()
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bfd peers to go up")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=16, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_bgp_convergence():
+ "Assert that BGP is converging."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers to go up")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_summary.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp vrf {}-bfd-cust1 summary json".format(router.name),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=125, wait=1.0)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_convergence():
+ "Assert that BGP is converging before setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp peers converge")
+
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp vrf {}-bfd-cust1 json".format(router.name),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=1)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bfd_fast_convergence():
+ """
+ Assert that BFD notices the link down after simulating network
+ failure.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Disable r2-eth0 link
+ router2 = tgen.gears["r2"]
+ topotest.interface_set_status(
+ router2, "r2-eth0", ifaceaction=False, vrf_name="r2-bfd-cust1"
+ )
+
+ # Wait the minimum time we can before checking that BGP/BFD
+ # converged.
+ logger.info("waiting for BFD converge")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ json_file = "{}/{}/peers.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ # Load the same file as previous test, but expect R1 to be down.
+ if router.name == "r1":
+ for peer in expected:
+ if peer["peer"] == "192.168.0.2":
+ peer["status"] = "down"
+ else:
+ for peer in expected:
+ if peer["peer"] == "192.168.0.1":
+ peer["status"] = "down"
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=40, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert res is None, assertmsg
+
+
+def test_bgp_fast_reconvergence():
+ "Assert that BGP is converging after setting a link down."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for BGP re convergence")
+
+ # Check that BGP converged quickly.
+ for router in tgen.routers().values():
+ ref_file = "{}/{}/bgp_prefixes.json".format(CWD, router.name)
+ expected = json.loads(open(ref_file).read())
+
+ # Load the same file as previous test, but set networks to None
+ # to test absence.
+ if router.name == "r1":
+ expected["routes"]["10.254.254.2/32"] = None
+ expected["routes"]["10.254.254.3/32"] = None
+ expected["routes"]["10.254.254.4/32"] = None
+ else:
+ expected["routes"]["10.254.254.1/32"] = None
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp vrf {}-bfd-cust1 json".format(router.name),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=16, wait=1)
+ assertmsg = "{}: bgp did not converge".format(router.name)
+ assert res is None, assertmsg
+
+
+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/bfd_vrflite_topo1/__init__.py b/tests/topotests/bfd_vrflite_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/__init__.py
diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json
new file mode 100644
index 0000000..07e96d7
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/r1/bfd_peers_status.json
@@ -0,0 +1,96 @@
+[
+ {
+ "multihop":false,
+ "peer":"192.168.0.2",
+ "vrf":"vrf1",
+ "passive-mode":false,
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":1000,
+ "transmit-interval":1000,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "detect-multiplier":3,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50,
+ "remote-detect-multiplier":3
+ },
+ {
+ "multihop":true,
+ "peer":"192.0.2.2",
+ "local":"192.0.2.1",
+ "vrf":"vrf2",
+ "passive-mode":false,
+ "minimum-ttl":254,
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":1000,
+ "transmit-interval":1000,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "detect-multiplier":3,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50,
+ "remote-detect-multiplier":3
+ },
+ {
+ "multihop":false,
+ "peer":"192.168.0.2",
+ "vrf":"vrf2",
+ "passive-mode":false,
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":1000,
+ "transmit-interval":1000,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "detect-multiplier":3,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50,
+ "remote-detect-multiplier":3
+ },
+ {
+ "multihop":false,
+ "peer":"192.168.0.2",
+ "vrf":"default",
+ "passive-mode":false,
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":1000,
+ "transmit-interval":1000,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "detect-multiplier":3,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50,
+ "remote-detect-multiplier":3
+ },
+ {
+ "multihop":true,
+ "peer":"192.0.2.2",
+ "local":"192.0.2.1",
+ "vrf":"vrf1",
+ "passive-mode":false,
+ "minimum-ttl":254,
+ "status":"up",
+ "diagnostic":"ok",
+ "remote-diagnostic":"ok",
+ "receive-interval":1000,
+ "transmit-interval":1000,
+ "echo-receive-interval":50,
+ "echo-transmit-interval":0,
+ "detect-multiplier":3,
+ "remote-receive-interval":1000,
+ "remote-transmit-interval":1000,
+ "remote-echo-receive-interval":50,
+ "remote-detect-multiplier":3
+ }
+]
diff --git a/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf
new file mode 100644
index 0000000..96e8ff4
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/r1/bfdd.conf
@@ -0,0 +1,26 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.2 vrf vrf1
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.168.0.2 vrf vrf2
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.168.0.2
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf1
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.0.2.2 multihop local-address 192.0.2.1 vrf vrf2
+ transmit-interval 1000
+ receive-interval 1000
+ ! \ No newline at end of file
diff --git a/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf
new file mode 100644
index 0000000..ebb4e63
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/r1/zebra.conf
@@ -0,0 +1,24 @@
+vrf vrf1
+ ip route 192.0.2.2/32 192.168.0.2
+!
+vrf vrf2
+ ip route 192.0.2.2/32 192.168.0.2
+!
+interface r1-eth0 vrf vrf3
+ ip address 192.168.0.1/24
+!
+interface r1-eth0.100 vrf vrf1
+ ip address 192.168.0.1/24
+!
+interface r1-eth0.200 vrf vrf2
+ ip address 192.168.0.1/24
+!
+interface r1-eth0.300
+ ip address 192.168.0.1/24
+!
+interface r1-loop1 vrf vrf1
+ ip address 192.0.2.1/32
+!
+interface r1-loop2 vrf vrf2
+ ip address 192.0.2.1/32
+! \ No newline at end of file
diff --git a/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf
new file mode 100644
index 0000000..7b11a47
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/r2/bfdd.conf
@@ -0,0 +1,26 @@
+!
+! debug bfd network
+! debug bfd peer
+! debug bfd zebra
+!
+bfd
+ peer 192.168.0.1 vrf vrf1
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.168.0.1 vrf vrf2
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.168.0.1
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf1
+ transmit-interval 1000
+ receive-interval 1000
+ !
+ peer 192.0.2.1 multihop local-address 192.0.2.2 vrf vrf2
+ transmit-interval 1000
+ receive-interval 1000
+ ! \ No newline at end of file
diff --git a/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf
new file mode 100644
index 0000000..d8b996e
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/r2/zebra.conf
@@ -0,0 +1,24 @@
+vrf vrf1
+ ip route 192.0.2.1/32 192.168.0.1
+!
+vrf vrf2
+ ip route 192.0.2.1/32 192.168.0.1
+!
+interface r2-eth0 vrf vrf3
+ ip address 192.168.0.2/24
+!
+interface r2-eth0.100 vrf vrf1
+ ip address 192.168.0.2/24
+!
+interface r2-eth0.200 vrf vrf2
+ ip address 192.168.0.2/24
+!
+interface r2-eth0.300
+ ip address 192.168.0.2/24
+!
+interface r2-loop1 vrf vrf1
+ ip address 192.0.2.2/32
+!
+interface r2-loop2 vrf vrf2
+ ip address 192.0.2.2/32
+! \ No newline at end of file
diff --git a/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py
new file mode 100644
index 0000000..b7afb8e
--- /dev/null
+++ b/tests/topotests/bfd_vrflite_topo1/test_bfd_vrflite_topo1.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+
+#
+# test_bfd_vrflite_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+# Copyright (c) 2022 by 6WIND
+#
+# 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_vrflite_topo1.py: Test the FRR BFD daemon.
+"""
+
+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.
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ # Create 2 routers
+ for routern in range(1, 3):
+ 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 = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ logger.info("Testing with Linux VRF support and udp_l3mdev=0")
+ if os.system("echo 0 > /proc/sys/net/ipv4/udp_l3mdev_accept") != 0:
+ return pytest.skip(
+ "Skipping BFD vrflite Topo1 Test. Linux VRF not available on System"
+ )
+
+ for rname, router in router_list.items():
+ router.net.add_l3vrf("vrf1", 10)
+ router.net.add_l3vrf("vrf2", 20)
+ router.net.add_l3vrf("vrf3", 30)
+ router.net.add_vlan(rname + "-eth0.100", rname + "-eth0", 100)
+ router.net.add_vlan(rname + "-eth0.200", rname + "-eth0", 200)
+ router.net.add_vlan(rname + "-eth0.300", rname + "-eth0", 300)
+ router.net.attach_iface_to_l3vrf(rname + "-eth0.100", "vrf1")
+ router.net.attach_iface_to_l3vrf(rname + "-eth0.200", "vrf2")
+ router.net.add_loop(rname + "-loop1")
+ router.net.add_loop(rname + "-loop2")
+ router.net.attach_iface_to_l3vrf(rname + "-loop1", "vrf1")
+ router.net.attach_iface_to_l3vrf(rname + "-loop2", "vrf2")
+ router.net.attach_iface_to_l3vrf(rname + "-eth0", "vrf3")
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # Move interfaces out of vrf namespace and delete the namespace
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.net.del_iface(rname + "-eth0.100")
+ router.net.del_iface(rname + "-eth0.200")
+ router.net.del_iface(rname + "-eth0.300")
+ router.net.del_iface(rname + "-loop1")
+ router.net.del_iface(rname + "-loop2")
+
+ tgen.stop_topology()
+
+
+def test_bfd_connection():
+ "Assert that the BFD peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("waiting for bfd peers to go up")
+ router = tgen.gears['r1']
+ json_file = "{}/{}/bfd_peers_status.json".format(CWD, 'r1')
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bfd peers json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=16, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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_aggregate_address_matching_med/__init__.py b/tests/topotests/bgp_aggregate_address_matching_med/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/__init__.py
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf
new file mode 100644
index 0000000..3559760
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r1/bgpd.conf
@@ -0,0 +1,21 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.2 route-map r2 out
+ exit-address-family
+!
+ip prefix-list p1 seq 5 permit 172.16.255.1/32
+ip prefix-list p1 seq 10 permit 172.16.255.2/32
+ip prefix-list p2 seq 15 permit 172.16.255.3/32
+!
+route-map r2 permit 10
+ match ip address prefix-list p1
+ set metric 300
+route-map r2 permit 20
+ match ip address prefix-list p2
+ set metric 400
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf
new file mode 100644
index 0000000..685adb3
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r1/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+ ip address 172.16.255.2/32
+ ip address 172.16.255.3/32
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf
new file mode 100644
index 0000000..9bc9a31
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r2/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+ address-family ipv4 unicast
+ aggregate-address 172.16.255.0/24 summary-only matching-MED-only
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf
new file mode 100644
index 0000000..f229954
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf b/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf
new file mode 100644
index 0000000..dfb5ac7
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r3/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf b/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf
new file mode 100644
index 0000000..11e06d4
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py b/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py
new file mode 100644
index 0000000..edf50dc
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_matching_med/test_bgp_aggregate_address_matching_med.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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 if aggregate-address command works fine when suppressing summary-only
+and using matching-MED-only together.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+from lib.common_config import (
+ step,
+)
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+
+def build_topo(tgen):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_aggregate_address_matching_med():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_converge():
+ output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.0/24": None,
+ "172.16.255.1/32": [{"path": "65002 65001"}],
+ "172.16.255.2/32": [{"path": "65002 65001"}],
+ "172.16.255.3/32": [{"path": "65002 65001"}],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see unsuppressed routes from R2"
+
+ step("Change MED for 172.16.255.3/32 from 400 to 300")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ route-map r2 permit 20
+ set metric 300
+ """
+ )
+
+ step("Check if 172.16.255.0/24 aggregated route was created and others suppressed")
+
+ def _bgp_aggregated_summary_only_med_match():
+ output = json.loads(r3.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.0/24": [{"path": "65002"}],
+ "172.16.255.1/32": None,
+ "172.16.255.2/32": None,
+ "172.16.255.3/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_aggregated_summary_only_med_match)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see unsuppressed routes from R2"
+
+ step("Change MED for 172.16.255.3/32 back to 400 from 300")
+ r1.vtysh_cmd(
+ """
+ configure terminal
+ route-map r2 permit 20
+ set metric 400
+ """
+ )
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see unsuppressed routes from R2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_aggregate_address_origin/__init__.py b/tests/topotests/bgp_aggregate_address_origin/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/__init__.py
diff --git a/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf
new file mode 100644
index 0000000..3486c64
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/r1/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ aggregate-address 172.16.255.0/24 origin igp
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf
new file mode 100644
index 0000000..b2d9455
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py b/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py
new file mode 100644
index 0000000..0d01fa2
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_origin/test_bgp_aggregate-address_origin.py
@@ -0,0 +1,119 @@
+#!/usr/bin/env python
+
+#
+# bgp_aggregate-address_origin.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+bgp_aggregate-address_origin.py:
+
+Test if works the following commands:
+router bgp 65031
+ address-family ipv4 unicast
+ aggregate-address 192.168.255.0/24 origin igp
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_aggregate_address_origin():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_aggregate_address_has_metric(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json"))
+ expected = {"paths": [{"origin": "IGP"}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_aggregate_address_has_metric, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Failed to see applied ORIGIN (igp) for aggregated prefix in "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_aggregate_address_route_map/__init__.py b/tests/topotests/bgp_aggregate_address_route_map/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/__init__.py
diff --git a/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf
new file mode 100644
index 0000000..7fb55cf
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ aggregate-address 172.16.255.0/24 route-map aggr-rmap
+ exit-address-family
+!
+route-map aggr-rmap permit 10
+ set metric 123
+!
diff --git a/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf
new file mode 100644
index 0000000..b2d9455
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py
new file mode 100644
index 0000000..df20594
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_route_map/test_bgp_aggregate-address_route-map.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+#
+# bgp_aggregate-address_route-map.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+bgp_aggregate-address_route-map.py:
+
+Test if works the following commands:
+router bgp 65031
+ address-family ipv4 unicast
+ aggregate-address 192.168.255.0/24 route-map aggr-rmap
+
+route-map aggr-rmap permit 10
+ set metric 123
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_invalid():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_aggregate_address_has_metric(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.0/24 json"))
+ expected = {"paths": [{"metric": 123}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_aggregate_address_has_metric, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Failed to see applied metric for aggregated prefix in "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_aggregate_address_topo1/__init__.py b/tests/topotests/bgp_aggregate_address_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/__init__.py
diff --git a/tests/topotests/bgp_aggregate_address_topo1/exabgp.env b/tests/topotests/bgp_aggregate_address_topo1/exabgp.env
new file mode 100644
index 0000000..28e6423
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/exabgp.env
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg
new file mode 100644
index 0000000..e0f6ab6
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/peer1/exabgp.cfg
@@ -0,0 +1,21 @@
+neighbor 10.0.0.1 {
+ router-id 10.254.254.3;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65000;
+ static {
+ route 10.254.254.3/32 next-hop 10.0.0.2;
+
+ route 192.168.0.1/32 next-hop 10.0.0.2 med 10;
+ route 192.168.0.2/32 next-hop 10.0.0.2 med 10;
+ route 192.168.0.3/32 next-hop 10.0.0.2 med 10;
+
+ route 192.168.1.1/32 next-hop 10.0.0.2 med 10;
+ route 192.168.1.2/32 next-hop 10.0.0.2 med 10;
+ route 192.168.1.3/32 next-hop 10.0.0.2 med 20;
+
+ route 192.168.2.1/32 next-hop 10.0.0.2;
+ route 192.168.2.2/32 next-hop 10.0.0.2;
+ route 192.168.2.3/32 next-hop 10.0.0.2;
+ }
+}
diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..c7cf4a5
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/r1/bgpd.conf
@@ -0,0 +1,30 @@
+! debug bgp updates
+!
+access-list acl-sup-one seq 5 permit 192.168.2.1/32
+access-list acl-sup-one seq 10 deny any
+!
+access-list acl-sup-two seq 5 permit 192.168.2.2/32
+access-list acl-sup-two seq 10 deny any
+!
+access-list acl-sup-three seq 5 permit 192.168.2.3/32
+access-list acl-sup-three seq 10 deny any
+!
+route-map rm-sup-one permit 10
+ match ip address acl-sup-one
+!
+route-map rm-sup-two permit 10
+ match ip address acl-sup-two
+!
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as 65001
+ neighbor 10.0.0.2 timers 3 10
+ neighbor 10.0.1.2 remote-as internal
+ neighbor 10.0.1.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ aggregate-address 192.168.0.0/24 matching-MED-only
+ aggregate-address 192.168.1.0/24 matching-MED-only
+ aggregate-address 192.168.2.0/24 suppress-map rm-sup-one
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf b/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf
new file mode 100644
index 0000000..931c73d
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/r1/zebra.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+ipv6 forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf b/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..acacd86
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/r2/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65000
+ neighbor 10.0.1.1 remote-as internal
+ neighbor 10.0.1.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf b/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf
new file mode 100644
index 0000000..38e0c44
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/r2/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
diff --git a/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
new file mode 100644
index 0000000..f506792
--- /dev/null
+++ b/tests/topotests/bgp_aggregate_address_topo1/test_bgp_aggregate_address_topo1.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_aggregate_address_topo1.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 BGP aggregate address features.
+"""
+
+import os
+import sys
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(peer1)
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(r1)
+ switch.add_link(r2)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+ router = tgen.gears["r2"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r2/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r2/bgpd.conf"))
+ router.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def expect_route(router_name, routes_expected):
+ "Helper function to avoid repeated code."
+ tgen = get_topogen()
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ tgen.gears[router_name],
+ "show ip route json",
+ routes_expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=120, wait=1)
+ assertmsg = '"{}" BGP convergence failure'.format(router_name)
+ assert result is None, assertmsg
+
+
+def test_expect_convergence():
+ "Test that BGP protocol converged."
+
+ 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 = functools.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 = '"{}" BGP convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
+
+
+def test_bgp_aggregate_address_matching_med_only():
+ "Test that the command matching-MED-only works."
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ routes_expected = {
+ # All MED matches, aggregation must exist.
+ "192.168.0.0/24": [{"protocol": "bgp", "metric": 0}],
+ "192.168.0.1/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.0.2/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.0.3/32": [{"protocol": "bgp", "metric": 10}],
+ # Non matching MED: aggregation must not exist.
+ "192.168.1.0/24": None,
+ "192.168.1.1/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.1.2/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.1.3/32": [{"protocol": "bgp", "metric": 20}],
+ }
+
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ tgen.gears["r2"],
+ "show ip route json",
+ routes_expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"r2" BGP convergence failure'
+ assert result is None, assertmsg
+
+
+def test_bgp_aggregate_address_match_and_suppress():
+ "Test that the command matching-MED-only with suppression works."
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_multicmd(
+ """
+configure terminal
+router bgp 65000
+address-family ipv4 unicast
+no aggregate-address 192.168.0.0/24 matching-MED-only
+no aggregate-address 192.168.1.0/24 matching-MED-only
+aggregate-address 192.168.0.0/24 matching-MED-only summary-only
+aggregate-address 192.168.1.0/24 matching-MED-only summary-only
+"""
+ )
+
+ routes_expected = {
+ # All MED matches, aggregation must exist.
+ "192.168.0.0/24": [{"protocol": "bgp", "metric": 0}],
+ "192.168.0.1/32": None,
+ "192.168.0.2/32": None,
+ "192.168.0.3/32": None,
+ # Non matching MED: aggregation must not exist.
+ "192.168.1.0/24": None,
+ "192.168.1.1/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.1.2/32": [{"protocol": "bgp", "metric": 10}],
+ "192.168.1.3/32": [{"protocol": "bgp", "metric": 20}],
+ }
+
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ tgen.gears["r2"],
+ "show ip route json",
+ routes_expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=120, wait=1)
+ assertmsg = '"r2" BGP convergence failure'
+ assert result is None, assertmsg
+
+
+def test_bgp_aggregate_address_suppress_map():
+ "Test that the command suppress-map works."
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ expect_route(
+ "r2",
+ {
+ "192.168.2.0/24": [{"protocol": "bgp"}],
+ "192.168.2.1/32": None,
+ "192.168.2.2/32": [{"protocol": "bgp"}],
+ "192.168.2.3/32": [{"protocol": "bgp"}],
+ },
+ )
+
+ # Change route map and test again.
+ tgen.gears["r1"].vtysh_multicmd(
+ """
+configure terminal
+router bgp 65000
+address-family ipv4 unicast
+no aggregate-address 192.168.2.0/24 suppress-map rm-sup-one
+aggregate-address 192.168.2.0/24 suppress-map rm-sup-two
+"""
+ )
+
+ expect_route(
+ "r2",
+ {
+ "192.168.2.0/24": [{"protocol": "bgp"}],
+ "192.168.2.1/32": [{"protocol": "bgp"}],
+ "192.168.2.2/32": None,
+ "192.168.2.3/32": [{"protocol": "bgp"}],
+ },
+ )
+
+
+def test_bgp_aggregate_address_suppress_map_update_route_map():
+ "Test that the suppress-map late route map creation works."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.gears["r1"].vtysh_multicmd(
+ """
+configure terminal
+router bgp 65000
+address-family ipv4 unicast
+no aggregate-address 192.168.2.0/24 suppress-map rm-sup-two
+aggregate-address 192.168.2.0/24 suppress-map rm-sup-three
+"""
+ )
+
+ expect_route(
+ "r2",
+ {
+ "192.168.2.0/24": [{"protocol": "bgp"}],
+ "192.168.2.1/32": [{"protocol": "bgp"}],
+ "192.168.2.2/32": [{"protocol": "bgp"}],
+ "192.168.2.3/32": [{"protocol": "bgp"}],
+ },
+ )
+
+ # Create missing route map and test again.
+ tgen.gears["r1"].vtysh_multicmd(
+ """
+configure terminal
+route-map rm-sup-three permit 10
+match ip address acl-sup-three
+"""
+ )
+
+ expect_route(
+ "r2",
+ {
+ "192.168.2.0/24": [{"protocol": "bgp"}],
+ "192.168.2.1/32": [{"protocol": "bgp"}],
+ "192.168.2.2/32": [{"protocol": "bgp"}],
+ "192.168.2.3/32": None,
+ },
+ )
+
+
+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_aggregator_zero/__init__.py b/tests/topotests/bgp_aggregator_zero/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/__init__.py
diff --git a/tests/topotests/bgp_aggregator_zero/exabgp.env b/tests/topotests/bgp_aggregator_zero/exabgp.env
new file mode 100644
index 0000000..28e6423
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/exabgp.env
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg b/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg
new file mode 100644
index 0000000..b3f2527
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/peer1/exabgp.cfg
@@ -0,0 +1,18 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65534;
+
+ static {
+ route 192.168.100.101/32 {
+ aggregator (0:10.0.0.2);
+ next-hop 10.0.0.2;
+ }
+
+ route 192.168.100.102/32 {
+ aggregator (65001:10.0.0.2);
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf b/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf
new file mode 100644
index 0000000..002a5c7
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65534
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_aggregator_zero/r1/zebra.conf b/tests/topotests/bgp_aggregator_zero/r1/zebra.conf
new file mode 100644
index 0000000..22a26ac
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py b/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py
new file mode 100644
index 0000000..ea71c82
--- /dev/null
+++ b/tests/topotests/bgp_aggregator_zero/test_bgp_aggregator_zero.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if BGP UPDATE with AGGREGATOR AS attribute with value zero (0)
+is continued to be processed, but AGGREGATOR attribute is discarded.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r1 = tgen.add_router("r1")
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(peer1)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_aggregator_zero():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json")
+ )
+ expected = {
+ "10.0.0.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"])
+
+ def _bgp_has_correct_aggregator_route_with_asn_0():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.101/32 json")
+ )
+
+ if "aggregatorAs" in output["paths"][0].keys():
+ return False
+ else:
+ return True
+
+ assert (
+ _bgp_has_correct_aggregator_route_with_asn_0() is True
+ ), 'Aggregator AS attribute with ASN 0 found in "{}"'.format(tgen.gears["r1"])
+
+ def _bgp_has_correct_aggregator_route_with_good_asn():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp 192.168.100.102/32 json")
+ )
+ expected = {"paths": [{"aggregatorAs": 65001, "aggregatorId": "10.0.0.2"}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_has_correct_aggregator_route_with_good_asn)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Aggregator AS attribute not found in "{}"'.format(
+ tgen.gears["r1"]
+ )
+
+
+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_as_allow_in/bgp_as_allow_in.json b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json
new file mode 100644
index 0000000..943876c
--- /dev/null
+++ b/tests/topotests/bgp_as_allow_in/bgp_as_allow_in.json
@@ -0,0 +1,266 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py
new file mode 100644
index 0000000..ec66c8c
--- /dev/null
+++ b/tests/topotests/bgp_as_allow_in/test_bgp_as_allow_in.py
@@ -0,0 +1,982 @@
+#!/usr/bin/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 bgp allowas-in functionality:
+
+- Verify that routes coming from same AS are accepted only when
+ '"allowas-in" is configuerd.
+- Verify that "allowas-in" feature works per address-family/VRF
+ 'basis and doesn't impact the other AFIs.
+- Verify that the if number of occurrences of AS number in path is
+ 'more than the configured allowas-in value then we do not accept
+ 'the route.
+- Verify that when we advertise a network, learned from the same AS
+ 'via allowas-in command, to an iBGP neighbor we see multiple
+ 'occurrences.
+- Verify that when we advertise a network, learned from the same AS
+ 'via allowas-in command, to an eBGP neighbor we see multiple
+ 'occurrences of our own AS based on configured value+1.
+"""
+
+import os
+import sys
+import time
+import json
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ create_route_maps,
+ check_address_types,
+ step,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/bgp_as_allow_in.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
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_bgp_allowas_in_p0(request):
+ """
+ Verify that routes coming from same AS are accepted only when
+ "allowas-in" is configuerd.
+
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).")
+ step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).")
+ # configure static routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ 'Check BGP table of router R3 using "sh bgp ipv4" and "sh bgp '
+ 'ipv6" command.'
+ )
+ step(
+ "We should not see prefix advertised from R1 in R3's BGP "
+ "table without allowas-in."
+ )
+ logger.info("Verifying %s routes on r3, route should not be present", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=NEXT_HOP_IP[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n".format(tc_name)
+ + "Expected behavior: routes should not present in rib \n"
+ + "Error: {}".format(result)
+ )
+
+ step("Configure allowas-in on R3 for R2.")
+ step("We should see the prefix advertised from R1 in R3's BGP table.")
+ # Api call to enable allowas-in in bgp process.
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {"number_occurences": 1}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_allowas_in_per_addr_family_p0(request):
+ """
+ Verify that "allowas-in" feature works per address-family/VRF
+ basis and doesn't impact the other AFIs.
+
+ """
+
+ # This test is applicable only for dual stack.
+ if "ipv4" not in ADDR_TYPES or "ipv6" not in ADDR_TYPES:
+ pytest.skip("NOT APPLICABLE")
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Advertise prefix 2.2.2.2/32 from Router-1(AS-200).")
+ step("Advertise an ipv6 prefix 22:22::2/128 from Router-1(AS-200).")
+ # configure static routes routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Configure allowas-in on R3 for R2 under IPv4 addr-family only")
+ # Api call to enable allowas-in in bgp process.
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"allowas-in": {"number_occurences": 1}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ static_route_ipv4 = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]}
+ ]
+ }
+ }
+
+ static_route_ipv6 = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]}
+ ]
+ }
+ }
+ step("We should see R1 advertised prefix only in IPv4 AFI " "not in IPv6 AFI.")
+ result = verify_rib(tgen, "ipv4", dut, static_route_ipv4, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(
+ tgen, "ipv6", dut, static_route_ipv6, protocol=protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n".format(tc_name)
+ + "Expected behavior: routes are should not be present in ipv6 rib\n"
+ + " Error: {}".format(result)
+ )
+
+ step("Repeat the same test for IPv6 AFI.")
+ step("Configure allowas-in on R3 for R2 under IPv6 addr-family only")
+ # Api call to enable allowas-in in bgp process.
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {
+ "number_occurences": 2,
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"allowas-in": {"number_occurences": 2}}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("We should see R1 advertised prefix only in IPv6 AFI " "not in IPv4 AFI.")
+ result = verify_rib(
+ tgen, "ipv4", dut, static_route_ipv4, protocol=protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n".format(tc_name)
+ + "Expected behavior: routes should not be present in ipv4 rib\n"
+ + " Error: {}".format(result)
+ )
+ result = verify_rib(tgen, "ipv6", dut, static_route_ipv6, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_allowas_in_no_of_occurrences_p0(request):
+ """
+ Verify that the if number of occurrences of AS number in path is
+ more than the configured allowas-in value then we do not accept
+ the route.
+
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Configure a route-map on R1 to prepend AS 4 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {
+ "as_num": "200 200 200 200",
+ "as_action": "prepend",
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure route map in out direction on R1")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "ASP_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ step('Configure "allowas-in 4" on R3 for R2.')
+ # Api call to enable allowas-in in bgp process.
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {"number_occurences": 4}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, protocol=protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n ".format(tc_name)
+ + "Expected behavior: routes are should not be present in rib\n"
+ + "Error: {}".format(result)
+ )
+
+ for addr_type in ADDR_TYPES:
+ step('Configure "allowas-in 5" on R3 for R2.')
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {"number_occurences": 5}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, dut, static_routes, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_allowas_in_sameastoibgp_p1(request):
+ """
+ Verify that when we advertise a network, learned from the same AS
+ via allowas-in command, to an iBGP neighbor we see multiple
+ occurrences.
+
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Configure a route-map on R2 to prepend AS 2 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "200 200", "as_action": "prepend"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure route map in out direction on R2")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "ASP_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step('Configure "allowas-in 3" on R3 for R1.')
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {"number_occurences": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "allowas-in": {"number_occurences": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ dut = "r4"
+ path = "100 200 200 200"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_allowas_in_sameastoebgp_p1(request):
+ """
+ Verify that when we advertise a network, learned from the same AS
+ via allowas-in command, to an eBGP neighbor we see multiple
+ occurrences of our own AS based on configured value+1.
+
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static in Router BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Configure a route-map on R2 to prepend AS 2 times.")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "200 200", "as_action": "prepend"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure route map in out direction on R2")
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "ASP_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ step('Configure "allowas-in 3" on R3 for R1.')
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "allowas-in": {"number_occurences": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+ dut = "r5"
+ path = "200 100 200 200 200"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, aspath=path)
+ 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/bgp_as_override/__init__.py b/tests/topotests/bgp_as_override/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_as_override/__init__.py
diff --git a/tests/topotests/bgp_as_override/r1/bgpd.conf b/tests/topotests/bgp_as_override/r1/bgpd.conf
new file mode 100644
index 0000000..3cfb7a2
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r1/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_as_override/r1/zebra.conf b/tests/topotests/bgp_as_override/r1/zebra.conf
new file mode 100644
index 0000000..63728eb
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_override/r2/bgpd.conf b/tests/topotests/bgp_as_override/r2/bgpd.conf
new file mode 100644
index 0000000..5e3b0c7
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r2/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+!
diff --git a/tests/topotests/bgp_as_override/r2/zebra.conf b/tests/topotests/bgp_as_override/r2/zebra.conf
new file mode 100644
index 0000000..5bdfd02
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r2-eth0
+ ip address 192.168.1.1/30
+!
+interface r2-eth1
+ ip address 192.168.2.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_override/r3/bgpd.conf b/tests/topotests/bgp_as_override/r3/bgpd.conf
new file mode 100644
index 0000000..6bbe56b
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r3/bgpd.conf
@@ -0,0 +1,13 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+ neighbor 192.168.3.1 remote-as external
+ neighbor 192.168.3.1 timers 1 3
+ neighbor 192.168.3.1 timers connect 1
+ address-family ipv4 unicast
+ neighbor 192.168.3.1 as-override
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_as_override/r3/zebra.conf b/tests/topotests/bgp_as_override/r3/zebra.conf
new file mode 100644
index 0000000..77782be
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r3-eth0
+ ip address 192.168.2.2/30
+!
+interface r3-eth1
+ ip address 192.168.3.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_override/r4/bgpd.conf b/tests/topotests/bgp_as_override/r4/bgpd.conf
new file mode 100644
index 0000000..1bdee08
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r4/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.3.2 remote-as external
+ neighbor 192.168.3.2 timers 1 3
+ neighbor 192.168.3.2 timers connect 1
+!
diff --git a/tests/topotests/bgp_as_override/r4/zebra.conf b/tests/topotests/bgp_as_override/r4/zebra.conf
new file mode 100644
index 0000000..71dc595
--- /dev/null
+++ b/tests/topotests/bgp_as_override/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.3.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_override/test_bgp_as_override.py b/tests/topotests/bgp_as_override/test_bgp_as_override.py
new file mode 100644
index 0000000..40085cd
--- /dev/null
+++ b/tests/topotests/bgp_as_override/test_bgp_as_override.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 7):
+ 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):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_as_override():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+
+ def _bgp_converge():
+ output = json.loads(r3.vtysh_cmd("show ip bgp neighbor 192.168.2.1 json"))
+ expected = {
+ "192.168.2.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_as_override():
+ output = json.loads(r4.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.1/32": [{"valid": True, "path": "65003 65002 65003"}]
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R4"
+
+ step("Check if BGP as-override from R3 works")
+ test_func = functools.partial(_bgp_as_override)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see overriden ASN (65001) from R3"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py b/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/__init__.py
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf
new file mode 100644
index 0000000..cb04749
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/bgpd.conf
@@ -0,0 +1,7 @@
+! exit1
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf
new file mode 100644
index 0000000..c060e14
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r1/zebra.conf
@@ -0,0 +1,6 @@
+! exit1
+interface r1-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf
new file mode 100644
index 0000000..bed6885
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/bgpd.conf
@@ -0,0 +1,9 @@
+! spine
+router bgp 65002
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.3 remote-as 65002
+ neighbor 192.168.255.3 timers 3 10
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf
new file mode 100644
index 0000000..a45520f
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r2/zebra.conf
@@ -0,0 +1,6 @@
+! spine
+interface r2-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf
new file mode 100644
index 0000000..384e617
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/bgpd.conf
@@ -0,0 +1,7 @@
+! exit2
+router bgp 65002
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf
new file mode 100644
index 0000000..2f4dbc5
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/r3/zebra.conf
@@ -0,0 +1,6 @@
+! exit2
+interface r3-eth0
+ ip address 192.168.255.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
new file mode 100644
index 0000000..571e28c
--- /dev/null
+++ b/tests/topotests/bgp_as_wide_bgp_identifier/test_bgp_as_wide_bgp_identifier.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_as_wide_bgp_identifier.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+rfc6286: Autonomous-System-Wide Unique BGP Identifier for BGP-4
+Test if 'Bad BGP Identifier' notification is sent only to
+internal peers (autonomous-system-wide). eBGP peers are not
+affected and should work.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_as_wide_bgp_identifier():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {"192.168.255.1": {"bgpState": "Established"}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_failed(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "lastNotificationReason": "OPEN Message Error/Bad BGP Identifier"
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, tgen.gears["r1"])
+ success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5)
+
+ assert result is None, 'Failed to converge: "{}"'.format(tgen.gears["r1"])
+
+ test_func = functools.partial(_bgp_failed, tgen.gears["r3"])
+ success, result = topotest.run_and_expect(test_func, None, count=260, wait=0.5)
+
+ assert result is None, 'Bad BGP Identifier notification not sent: "{}"'.format(
+ tgen.gears["r3"]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_aspath_zero/__init__.py b/tests/topotests/bgp_aspath_zero/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/__init__.py
diff --git a/tests/topotests/bgp_aspath_zero/exabgp.env b/tests/topotests/bgp_aspath_zero/exabgp.env
new file mode 100644
index 0000000..28e6423
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/exabgp.env
@@ -0,0 +1,53 @@
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg
new file mode 100644
index 0000000..fe9ea01
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/peer1/exabgp.cfg
@@ -0,0 +1,17 @@
+neighbor 10.0.0.1 {
+ router-id 10.0.0.2;
+ local-address 10.0.0.2;
+ local-as 65001;
+ peer-as 65534;
+
+ static {
+ route 192.168.100.101/32 {
+ next-hop 10.0.0.2;
+ }
+
+ route 192.168.100.102/32 {
+ as-path [65000 0 65001];
+ next-hop 10.0.0.2;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_aspath_zero/r1/bgpd.conf b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf
new file mode 100644
index 0000000..002a5c7
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65534
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as external
+ neighbor 10.0.0.2 timers 3 10
+!
diff --git a/tests/topotests/bgp_aspath_zero/r1/zebra.conf b/tests/topotests/bgp_aspath_zero/r1/zebra.conf
new file mode 100644
index 0000000..22a26ac
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py
new file mode 100644
index 0000000..14689d7
--- /dev/null
+++ b/tests/topotests/bgp_aspath_zero/test_bgp_aspath_zero.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if BGP UPDATE with AS-PATH attribute with value zero (0)
+is threated as withdrawal.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r1 = tgen.add_router("r1")
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.2", defaultRoute="via 10.0.0.1")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(peer1)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+ peer = tgen.gears["peer1"]
+ peer.start(os.path.join(CWD, "peer1"), os.path.join(CWD, "exabgp.env"))
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_aggregator_zero():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 10.0.0.2 json")
+ )
+ expected = {
+ "10.0.0.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "More than one prefix seen at r1, SHOULD be only one."
+
+ def _bgp_has_correct_routes_without_asn_0():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp json"))
+ expected = {"routes": {"192.168.100.101/32": [{"valid": True}]}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_has_correct_routes_without_asn_0)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed listing 192.168.100.101/32, SHOULD be accepted."
+
+
+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_auth/R1/bgpd.conf b/tests/topotests/bgp_auth/R1/bgpd.conf
new file mode 100644
index 0000000..310841f
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65001
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 password hello1
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 password hello2
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ address-family ipv4 unicast
+ neighbor 2.2.2.2 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf
new file mode 100644
index 0000000..644e01c
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf.conf
@@ -0,0 +1,39 @@
+! debug bgp neighbor-events
+
+router bgp 65001 vrf blue
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 password blue1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password blue2
+ address-family ipv4 unicast
+ neighbor 2.2.2.2 activate
+ neighbor 3.3.3.3 activate
+
+router bgp 65001 vrf red
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo2
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 password red1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo2
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password red2
+ address-family ipv4 unicast
+ neighbor 2.2.2.2 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf
new file mode 100644
index 0000000..7e15720
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd_multi_vrf_prefix.conf
@@ -0,0 +1,37 @@
+router bgp 65001 vrf blue
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor TWO_GROUP_blue peer-group
+ neighbor TWO_GROUP_blue remote-as 65002
+ neighbor TWO_GROUP_blue update-source 1.1.1.1
+ neighbor TWO_GROUP_blue ebgp-multihop 3
+ neighbor TWO_GROUP_blue password blue1
+ neighbor THREE_GROUP_blue peer-group
+ neighbor THREE_GROUP_blue remote-as 65003
+ neighbor THREE_GROUP_blue update-source 1.1.1.1
+ neighbor THREE_GROUP_blue ebgp-multihop 3
+ neighbor THREE_GROUP_blue password blue2
+ bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue
+ bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue
+ address-family ipv4 unicast
+ neighbor TWO_GROUP_blue maximum-prefix 4294967295
+ neighbor THREE_GROUP_blue maximum-prefix 4294967295
+
+router bgp 65001 vrf red
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor TWO_GROUP_red peer-group
+ neighbor TWO_GROUP_red remote-as 65002
+ neighbor TWO_GROUP_red update-source 1.1.1.1
+ neighbor TWO_GROUP_red ebgp-multihop 3
+ neighbor TWO_GROUP_red password red1
+ neighbor THREE_GROUP_red peer-group
+ neighbor THREE_GROUP_red remote-as 65003
+ neighbor THREE_GROUP_red update-source 1.1.1.1
+ neighbor THREE_GROUP_red ebgp-multihop 3
+ neighbor THREE_GROUP_red password red2
+ bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_red
+ bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_red
+ address-family ipv4 unicast
+ neighbor TWO_GROUP_red maximum-prefix 4294967295
+ neighbor THREE_GROUP_red maximum-prefix 4294967295
diff --git a/tests/topotests/bgp_auth/R1/bgpd_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_prefix.conf
new file mode 100644
index 0000000..9200b05
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65001
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor TWO_GROUP peer-group
+ neighbor TWO_GROUP remote-as 65002
+ neighbor TWO_GROUP update-source 1.1.1.1
+ neighbor TWO_GROUP ebgp-multihop 3
+ neighbor TWO_GROUP password hello1
+ neighbor THREE_GROUP peer-group
+ neighbor THREE_GROUP remote-as 65003
+ neighbor THREE_GROUP update-source 1.1.1.1
+ neighbor THREE_GROUP ebgp-multihop 3
+ neighbor THREE_GROUP password hello2
+ bgp listen range 2.2.2.0/24 peer-group TWO_GROUP
+ bgp listen range 3.3.3.0/24 peer-group THREE_GROUP
+ address-family ipv4 unicast
+ neighbor TWO_GROUP maximum-prefix 4294967295
+ neighbor THREE_GROUP maximum-prefix 4294967295
diff --git a/tests/topotests/bgp_auth/R1/bgpd_vrf.conf b/tests/topotests/bgp_auth/R1/bgpd_vrf.conf
new file mode 100644
index 0000000..5799da1
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd_vrf.conf
@@ -0,0 +1,20 @@
+! debug bgp neighbor-events
+
+router bgp 65001 vrf blue
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 password hello1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password hello2
+ address-family ipv4 unicast
+ neighbor 2.2.2.2 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf
new file mode 100644
index 0000000..d68951b
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/bgpd_vrf_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65001 vrf blue
+ timers bgp 3 9
+ bgp router-id 1.1.1.1
+ neighbor TWO_GROUP_blue peer-group
+ neighbor TWO_GROUP_blue remote-as 65002
+ neighbor TWO_GROUP_blue update-source 1.1.1.1
+ neighbor TWO_GROUP_blue ebgp-multihop 3
+ neighbor TWO_GROUP_blue password hello1
+ neighbor THREE_GROUP_blue peer-group
+ neighbor THREE_GROUP_blue remote-as 65003
+ neighbor THREE_GROUP_blue update-source 1.1.1.1
+ neighbor THREE_GROUP_blue ebgp-multihop 3
+ neighbor THREE_GROUP_blue password hello2
+ bgp listen range 2.2.2.0/24 peer-group TWO_GROUP_blue
+ bgp listen range 3.3.3.0/24 peer-group THREE_GROUP_blue
+ address-family ipv4 unicast
+ neighbor TWO_GROUP_blue maximum-prefix 4294967295
+ neighbor THREE_GROUP_blue maximum-prefix 4294967295
diff --git a/tests/topotests/bgp_auth/R1/empty.conf b/tests/topotests/bgp_auth/R1/empty.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/empty.conf
diff --git a/tests/topotests/bgp_auth/R1/ospfd.conf b/tests/topotests/bgp_auth/R1/ospfd.conf
new file mode 100644
index 0000000..b28dd59
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/ospfd.conf
@@ -0,0 +1,22 @@
+interface R1-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf
+ network 10.10.0.0/16 area 0
+ network 10.20.0.0/16 area 0
+ network 1.1.1.1/32 area 0 \ No newline at end of file
diff --git a/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf
new file mode 100644
index 0000000..b64bec8
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/ospfd_multi_vrf.conf
@@ -0,0 +1,26 @@
+interface R1-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.10.0.0/16 area 0
+ network 10.20.0.0/16 area 0
+ network 1.1.1.1/32 area 0
+router ospf vrf red
+ network 10.10.0.0/16 area 0
+ network 10.20.0.0/16 area 0
+ network 1.1.1.1/32 area 0
diff --git a/tests/topotests/bgp_auth/R1/ospfd_vrf.conf b/tests/topotests/bgp_auth/R1/ospfd_vrf.conf
new file mode 100644
index 0000000..deaf53d
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/ospfd_vrf.conf
@@ -0,0 +1,22 @@
+interface R1-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R1-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.10.0.0/16 area 0
+ network 10.20.0.0/16 area 0
+ network 1.1.1.1/32 area 0
diff --git a/tests/topotests/bgp_auth/R1/zebra.conf b/tests/topotests/bgp_auth/R1/zebra.conf
new file mode 100644
index 0000000..a0b062c
--- /dev/null
+++ b/tests/topotests/bgp_auth/R1/zebra.conf
@@ -0,0 +1,20 @@
+!
+interface lo
+ ip address 1.1.1.1/32
+interface lo1 vrf blue
+ ip address 1.1.1.1/32
+interface lo2 vrf red
+ ip address 1.1.1.1/32
+interface R1-eth0
+ ip address 10.10.0.1/24
+interface R1-eth1
+ ip address 10.20.0.1/24
+interface R1-eth2 vrf blue
+ ip address 10.10.0.1/24
+interface R1-eth3 vrf blue
+ ip address 10.20.0.1/24
+interface R1-eth4 vrf red
+ ip address 10.10.0.1/24
+interface R1-eth5 vrf red
+ ip address 10.20.0.1/24
+!
diff --git a/tests/topotests/bgp_auth/R2/bgpd.conf b/tests/topotests/bgp_auth/R2/bgpd.conf
new file mode 100644
index 0000000..2149c05
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65002
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf
new file mode 100644
index 0000000..af88fe1
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf.conf
@@ -0,0 +1,37 @@
+router bgp 65002 vrf blue
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password blue1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password blue3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
+
+router bgp 65002 vrf red
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo2
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password red1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo2
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password red3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf
new file mode 100644
index 0000000..af88fe1
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd_multi_vrf_prefix.conf
@@ -0,0 +1,37 @@
+router bgp 65002 vrf blue
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password blue1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password blue3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
+
+router bgp 65002 vrf red
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo2
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password red1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo2
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password red3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/bgpd_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_prefix.conf
new file mode 100644
index 0000000..2149c05
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65002
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/bgpd_vrf.conf b/tests/topotests/bgp_auth/R2/bgpd_vrf.conf
new file mode 100644
index 0000000..03cadb3
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd_vrf.conf
@@ -0,0 +1,18 @@
+router bgp 65002 vrf blue
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf
new file mode 100644
index 0000000..03cadb3
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/bgpd_vrf_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65002 vrf blue
+ timers bgp 3 9
+ bgp router-id 2.2.2.2
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello1
+ neighbor 3.3.3.3 remote-as 65003
+ neighbor 3.3.3.3 update-source lo1
+ neighbor 3.3.3.3 ebgp-multihop 3
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 timers connect 5
+ neighbor 3.3.3.3 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 3.3.3.3 activate
diff --git a/tests/topotests/bgp_auth/R2/empty.conf b/tests/topotests/bgp_auth/R2/empty.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/empty.conf
diff --git a/tests/topotests/bgp_auth/R2/ospfd.conf b/tests/topotests/bgp_auth/R2/ospfd.conf
new file mode 100644
index 0000000..78e78d6
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/ospfd.conf
@@ -0,0 +1,22 @@
+interface R2-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf
+ network 10.10.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 2.2.2.2/32 area 0
diff --git a/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf
new file mode 100644
index 0000000..81eb5d6
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/ospfd_multi_vrf.conf
@@ -0,0 +1,26 @@
+interface R2-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.10.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 2.2.2.2/32 area 0
+router ospf vrf red
+ network 10.10.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 2.2.2.2/32 area 0
diff --git a/tests/topotests/bgp_auth/R2/ospfd_vrf.conf b/tests/topotests/bgp_auth/R2/ospfd_vrf.conf
new file mode 100644
index 0000000..673d103
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/ospfd_vrf.conf
@@ -0,0 +1,22 @@
+interface R2-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R2-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.10.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 2.2.2.2/32 area 0
diff --git a/tests/topotests/bgp_auth/R2/zebra.conf b/tests/topotests/bgp_auth/R2/zebra.conf
new file mode 100644
index 0000000..fed4c27
--- /dev/null
+++ b/tests/topotests/bgp_auth/R2/zebra.conf
@@ -0,0 +1,20 @@
+!
+interface lo
+ ip address 2.2.2.2/32
+interface lo1 vrf blue
+ ip address 2.2.2.2/32
+interface lo2 vrf red
+ ip address 2.2.2.2/32
+interface R2-eth0
+ ip address 10.10.0.2/24
+interface R2-eth1
+ ip address 10.30.0.2/24
+interface R2-eth2 vrf blue
+ ip address 10.10.0.2/24
+interface R2-eth3 vrf blue
+ ip address 10.30.0.2/24
+interface R2-eth4 vrf red
+ ip address 10.10.0.2/24
+interface R2-eth5 vrf red
+ ip address 10.30.0.2/24
+!
diff --git a/tests/topotests/bgp_auth/R3/bgpd.conf b/tests/topotests/bgp_auth/R3/bgpd.conf
new file mode 100644
index 0000000..ca9b838
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65003
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf
new file mode 100644
index 0000000..81d0299
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf.conf
@@ -0,0 +1,37 @@
+router bgp 65003 vrf blue
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password blue2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password blue3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
+
+router bgp 65003 vrf red
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo2
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password red2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo2
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password red3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf
new file mode 100644
index 0000000..81d0299
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd_multi_vrf_prefix.conf
@@ -0,0 +1,37 @@
+router bgp 65003 vrf blue
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password blue2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password blue3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
+
+router bgp 65003 vrf red
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo2
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password red2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo2
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password red3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/bgpd_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_prefix.conf
new file mode 100644
index 0000000..ca9b838
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65003
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/bgpd_vrf.conf b/tests/topotests/bgp_auth/R3/bgpd_vrf.conf
new file mode 100644
index 0000000..f8323e0
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd_vrf.conf
@@ -0,0 +1,18 @@
+router bgp 65003 vrf blue
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf b/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf
new file mode 100644
index 0000000..f8323e0
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/bgpd_vrf_prefix.conf
@@ -0,0 +1,18 @@
+router bgp 65003 vrf blue
+ timers bgp 3 9
+ bgp router-id 3.3.3.3
+ neighbor 1.1.1.1 remote-as 65001
+ neighbor 1.1.1.1 update-source lo1
+ neighbor 1.1.1.1 ebgp-multihop 3
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 timers connect 5
+ neighbor 1.1.1.1 password hello2
+ neighbor 2.2.2.2 remote-as 65002
+ neighbor 2.2.2.2 update-source lo1
+ neighbor 2.2.2.2 ebgp-multihop 3
+ neighbor 2.2.2.2 timers connect 5
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 password hello3
+ address-family ipv4 unicast
+ neighbor 1.1.1.1 activate
+ neighbor 2.2.2.2 activate
diff --git a/tests/topotests/bgp_auth/R3/empty.conf b/tests/topotests/bgp_auth/R3/empty.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/empty.conf
diff --git a/tests/topotests/bgp_auth/R3/ospfd.conf b/tests/topotests/bgp_auth/R3/ospfd.conf
new file mode 100644
index 0000000..befeadb
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/ospfd.conf
@@ -0,0 +1,22 @@
+interface R3-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 3.3.3.3/32 area 0
diff --git a/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf b/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf
new file mode 100644
index 0000000..2b2abc6
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/ospfd_multi_vrf.conf
@@ -0,0 +1,26 @@
+interface R3-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.20.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 3.3.3.3/32 area 0
+router ospf vrf red
+ network 10.20.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 3.3.3.3/32 area 0
diff --git a/tests/topotests/bgp_auth/R3/ospfd_vrf.conf b/tests/topotests/bgp_auth/R3/ospfd_vrf.conf
new file mode 100644
index 0000000..392d17a
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/ospfd_vrf.conf
@@ -0,0 +1,22 @@
+interface R3-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth2
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth3
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth4
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+interface R3-eth5
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+router ospf vrf blue
+ network 10.20.0.0/16 area 0
+ network 10.30.0.0/16 area 0
+ network 3.3.3.3/32 area 0
diff --git a/tests/topotests/bgp_auth/R3/zebra.conf b/tests/topotests/bgp_auth/R3/zebra.conf
new file mode 100644
index 0000000..d49c98b
--- /dev/null
+++ b/tests/topotests/bgp_auth/R3/zebra.conf
@@ -0,0 +1,20 @@
+!
+interface lo
+ ip address 3.3.3.3/32
+interface lo1 vrf blue
+ ip address 3.3.3.3/32
+interface lo2 vrf red
+ ip address 3.3.3.3/32
+interface R3-eth0
+ ip address 10.20.0.3/24
+interface R3-eth1
+ ip address 10.30.0.3/24
+interface R3-eth2 vrf blue
+ ip address 10.20.0.3/24
+interface R3-eth3 vrf blue
+ ip address 10.30.0.3/24
+interface R3-eth4 vrf red
+ ip address 10.20.0.3/24
+interface R3-eth5 vrf red
+ ip address 10.30.0.3/24
+!
diff --git a/tests/topotests/bgp_auth/bgp_auth_common.py b/tests/topotests/bgp_auth/bgp_auth_common.py
new file mode 100644
index 0000000..14e4d05
--- /dev/null
+++ b/tests/topotests/bgp_auth/bgp_auth_common.py
@@ -0,0 +1,279 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def vrf_str(vrf):
+ if vrf == "":
+ vrf_str = ""
+ else:
+ vrf_str = "vrf {}".format(vrf)
+
+ return vrf_str
+
+
+def peer_name(rtr, prefix, vrf):
+ "generate VRF string for CLI"
+ if vrf == "":
+ vrf_str = ""
+ else:
+ vrf_str = "_" + vrf
+
+ if prefix == "yes":
+ if rtr == "R2":
+ return "TWO_GROUP" + vrf_str
+ else:
+ return "THREE_GROUP" + vrf_str
+ else:
+ if rtr == "R2":
+ return "2.2.2.2"
+ else:
+ return "3.3.3.3"
+
+
+def print_diag(vrf):
+ "print failure disagnostics"
+
+ tgen = get_topogen()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ print(rname + ":")
+ print(router.vtysh_cmd("show run"))
+ print(router.vtysh_cmd("show ip route {}".format(vrf_str(vrf))))
+ print(router.vtysh_cmd("show bgp {} neighbor".format(vrf_str(vrf))))
+
+
+@common_config.retry(retry_timeout=190)
+def _check_neigh_state(router, peer, state, vrf=""):
+ "check BGP neighbor state on a router"
+
+ neigh_output = router.vtysh_cmd(
+ "show bgp {} neighbors {} json".format(vrf_str(vrf), peer)
+ )
+
+ peer_state = "Unknown"
+ neigh_output_json = json.loads(neigh_output)
+ if peer in neigh_output_json:
+ peer_state = neigh_output_json[peer]["bgpState"]
+ if peer_state == state:
+ return True
+ return "{} peer with {} expected state {} got {} ".format(
+ router.name, peer, state, peer_state
+ )
+
+
+def check_neigh_state(router, peer, state, vrf=""):
+ "check BGP neighbor state on a router"
+
+ assertmsg = _check_neigh_state(router, peer, state, vrf)
+ assert assertmsg is True, assertmsg
+
+
+def check_all_peers_established(vrf=""):
+ "standard check for extablished peers per vrf"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+ # do r1 last as he might be the dynamic one
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+
+def check_vrf_peer_remove_passwords(vrf="", prefix="no"):
+ "selectively remove passwords checking state"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
+ vrf_str(vrf), peer_name("R2", prefix, vrf)
+ )
+ )
+
+ check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nno neighbor {} password".format(
+ vrf_str(vrf), peer_name("R3", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nno neighbor 1.1.1.1 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nno neighbor 3.3.3.3 password".format(vrf_str(vrf))
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nno neighbor 2.2.2.2 password".format(vrf_str(vrf))
+ )
+ check_all_peers_established(vrf)
+
+
+def check_vrf_peer_change_passwords(vrf="", prefix="no"):
+ "selectively change passwords checking state"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nneighbor {} password change1".format(
+ vrf_str(vrf), peer_name("R2", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nneighbor 1.1.1.1 password change1".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
+
+ r1.vtysh_cmd(
+ "conf t\nrouter bgp 65001 {}\nneighbor {} password change2".format(
+ vrf_str(vrf), peer_name("R3", prefix, vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Established", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Connect", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ # don't check dynamic downed peers - they are removed
+ if prefix == "no":
+ check_neigh_state(r1, "3.3.3.3", "Connect", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nneighbor 1.1.1.1 password change2".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
+
+ r2.vtysh_cmd(
+ "conf t\nrouter bgp 65002 {}\nneighbor 3.3.3.3 password change3".format(
+ vrf_str(vrf)
+ )
+ )
+ check_neigh_state(r2, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r2, "3.3.3.3", "Connect", vrf)
+ check_neigh_state(r3, "1.1.1.1", "Established", vrf)
+ check_neigh_state(r3, "2.2.2.2", "Connect", vrf)
+ check_neigh_state(r1, "2.2.2.2", "Established", vrf)
+ check_neigh_state(r1, "3.3.3.3", "Established", vrf)
+
+ r3.vtysh_cmd(
+ "conf t\nrouter bgp 65003 {}\nneighbor 2.2.2.2 password change3".format(
+ vrf_str(vrf)
+ )
+ )
+ check_all_peers_established(vrf)
diff --git a/tests/topotests/bgp_auth/test_bgp_auth1.py b/tests/topotests/bgp_auth/test_bgp_auth1.py
new file mode 100644
index 0000000..5be07cf
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth1.py
@@ -0,0 +1,245 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+
+from bgp_auth_common import (
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+ check_vrf_peer_change_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_default_peer_established(tgen):
+ "default vrf 3 peers same password"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_all_peers_established()
+
+
+def test_default_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_vrf_peer_remove_passwords()
+
+
+def test_default_peer_change_passwords(tgen):
+ "selectively change passwords checking state"
+
+ reset_with_new_configs(tgen, "bgpd.conf", "ospfd.conf")
+ check_vrf_peer_change_passwords()
+
+
+def test_default_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_all_peers_established()
+
+
+def test_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_vrf_peer_remove_passwords(prefix="yes")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ 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_auth/test_bgp_auth2.py b/tests/topotests/bgp_auth/test_bgp_auth2.py
new file mode 100644
index 0000000..2b37e80
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth2.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_vrf_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_all_peers_established("blue")
+
+
+def test_vrf_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
+
+
+def test_vrf_prefix_peer_change_passwords(tgen):
+ "selectively change passwords checking state with VRF prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_vrf_prefix.conf", "ospfd_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
+
+
+def test_multiple_vrf_peer_established(tgen):
+ "default vrf 3 peers same password with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_all_peers_established("blue")
+ check_all_peers_established("red")
+
+
+def test_multiple_vrf_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_remove_passwords("blue")
+ check_all_peers_established("red")
+ check_vrf_peer_remove_passwords("red")
+ check_all_peers_established("blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ 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_auth/test_bgp_auth3.py b/tests/topotests/bgp_auth/test_bgp_auth3.py
new file mode 100644
index 0000000..220f350
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth3.py
@@ -0,0 +1,234 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_prefix_peer_change_passwords(tgen):
+ "selecively change passwords checkig state with prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_prefix.conf", "ospfd.conf")
+ check_vrf_peer_change_passwords(prefix="yes")
+
+
+def test_vrf_peer_established(tgen):
+ "default vrf 3 peers same password with VRF config"
+
+ # clean routers and load vrf config
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_all_peers_established("blue")
+
+
+def test_vrf_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with VRF config"
+
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue")
+
+
+def test_vrf_peer_change_passwords(tgen):
+ "selectively change passwords checking state with VRF config"
+
+ reset_with_new_configs(tgen, "bgpd_vrf.conf", "ospfd_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ 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_auth/test_bgp_auth4.py b/tests/topotests/bgp_auth/test_bgp_auth4.py
new file mode 100644
index 0000000..2b7a355
--- /dev/null
+++ b/tests/topotests/bgp_auth/test_bgp_auth4.py
@@ -0,0 +1,251 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_auth.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_auth.py: Test BGP Md5 Authentication
+
+ +------+
+ +--------| |--------+
+ | +------| R1 |------+ |
+ | | -----| |----+ | |
+ | | | +------+ | | |
+ | | | | | |
+ +------+ +------+
+ | |------------| |
+ | R2 |------------| R3 |
+ | |------------| |
+ +------+ +------+
+
+
+setup is 3 routers with 3 links between each each link in a different vrf
+Default, blue and red respectively
+Tests check various fiddling with passwords and checking that the peer
+establishment is as expected and passwords are not leaked across sockets
+for bgp instances
+"""
+# pylint: disable=C0413
+
+import json
+import os
+import platform
+import sys
+from time import sleep
+
+import pytest
+from lib import common_config, topotest
+from lib.common_config import (
+ save_initial_config_on_routers,
+ reset_with_new_configs,
+)
+from bgp_auth_common import (
+ check_vrf_peer_change_passwords,
+ check_all_peers_established,
+ check_vrf_peer_remove_passwords,
+)
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R2"])
+ tgen.add_link(tgen.gears["R1"], tgen.gears["R3"])
+ tgen.add_link(tgen.gears["R2"], tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+ r3 = tgen.gears["R3"]
+
+ # blue vrf
+ r1.cmd_raises("ip link add blue type vrf table 1001")
+ r1.cmd_raises("ip link set up dev blue")
+ r2.cmd_raises("ip link add blue type vrf table 1001")
+ r2.cmd_raises("ip link set up dev blue")
+ r3.cmd_raises("ip link add blue type vrf table 1001")
+ r3.cmd_raises("ip link set up dev blue")
+
+ r1.cmd_raises("ip link add lo1 type dummy")
+ r1.cmd_raises("ip link set lo1 master blue")
+ r1.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link add lo1 type dummy")
+ r2.cmd_raises("ip link set up dev lo1")
+ r2.cmd_raises("ip link set lo1 master blue")
+ r3.cmd_raises("ip link add lo1 type dummy")
+ r3.cmd_raises("ip link set up dev lo1")
+ r3.cmd_raises("ip link set lo1 master blue")
+
+ r1.cmd_raises("ip link set R1-eth2 master blue")
+ r1.cmd_raises("ip link set R1-eth3 master blue")
+ r2.cmd_raises("ip link set R2-eth2 master blue")
+ r2.cmd_raises("ip link set R2-eth3 master blue")
+ r3.cmd_raises("ip link set R3-eth2 master blue")
+ r3.cmd_raises("ip link set R3-eth3 master blue")
+
+ r1.cmd_raises("ip link set up dev R1-eth2")
+ r1.cmd_raises("ip link set up dev R1-eth3")
+ r2.cmd_raises("ip link set up dev R2-eth2")
+ r2.cmd_raises("ip link set up dev R2-eth3")
+ r3.cmd_raises("ip link set up dev R3-eth2")
+ r3.cmd_raises("ip link set up dev R3-eth3")
+
+ # red vrf
+ r1.cmd_raises("ip link add red type vrf table 1002")
+ r1.cmd_raises("ip link set up dev red")
+ r2.cmd_raises("ip link add red type vrf table 1002")
+ r2.cmd_raises("ip link set up dev red")
+ r3.cmd_raises("ip link add red type vrf table 1002")
+ r3.cmd_raises("ip link set up dev red")
+
+ r1.cmd_raises("ip link add lo2 type dummy")
+ r1.cmd_raises("ip link set lo2 master red")
+ r1.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link add lo2 type dummy")
+ r2.cmd_raises("ip link set up dev lo2")
+ r2.cmd_raises("ip link set lo2 master red")
+ r3.cmd_raises("ip link add lo2 type dummy")
+ r3.cmd_raises("ip link set up dev lo2")
+ r3.cmd_raises("ip link set lo2 master red")
+
+ r1.cmd_raises("ip link set R1-eth4 master red")
+ r1.cmd_raises("ip link set R1-eth5 master red")
+ r2.cmd_raises("ip link set R2-eth4 master red")
+ r2.cmd_raises("ip link set R2-eth5 master red")
+ r3.cmd_raises("ip link set R3-eth4 master red")
+ r3.cmd_raises("ip link set R3-eth5 master red")
+
+ r1.cmd_raises("ip link set up dev R1-eth4")
+ r1.cmd_raises("ip link set up dev R1-eth5")
+ r2.cmd_raises("ip link set up dev R2-eth4")
+ r2.cmd_raises("ip link set up dev R2-eth5")
+ r3.cmd_raises("ip link set up dev R3-eth4")
+ r3.cmd_raises("ip link set up dev R3-eth5")
+
+ r1.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r2.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+ r3.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+ router.load_config(TopoRouter.RD_BGP)
+
+ # After copying the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Save the initial router config. reset_config_on_routers will return to this config.
+ save_initial_config_on_routers(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_multiple_vrf_peer_change_passwords(tgen):
+ "selectively change passwords checking state with multiple VRFs"
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_change_passwords("blue")
+ check_all_peers_established("red")
+ check_vrf_peer_change_passwords("red")
+ check_all_peers_established("blue")
+
+
+def test_multiple_vrf_prefix_peer_established(tgen):
+ "default vrf 3 peers same password with multilpe VRFs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_all_peers_established("blue")
+ check_all_peers_established("red")
+
+
+def test_multiple_vrf_prefix_peer_remove_passwords(tgen):
+ "selectively remove passwords checking state with multiple vrfs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_remove_passwords(vrf="blue", prefix="yes")
+ check_all_peers_established("red")
+ check_vrf_peer_remove_passwords(vrf="red", prefix="yes")
+ check_all_peers_established("blue")
+
+
+def test_multiple_vrf_prefix_peer_change_passwords(tgen):
+ "selectively change passwords checking state with multiple vrfs and prefix config"
+
+ # only supported in kernel > 5.3
+ if topotest.version_cmp(platform.release(), "5.3") < 0:
+ return
+
+ reset_with_new_configs(tgen, "bgpd_multi_vrf_prefix.conf", "ospfd_multi_vrf.conf")
+ check_vrf_peer_change_passwords(vrf="blue", prefix="yes")
+ check_all_peers_established("red")
+ check_vrf_peer_change_passwords(vrf="red", prefix="yes")
+ check_all_peers_established("blue")
+
+
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+ 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_basic_functionality_topo1/__init__.py b/tests/topotests/bgp_basic_functionality_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_basic_functionality_topo1/__init__.py
diff --git a/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json b/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json
new file mode 100644
index 0000000..ee1f1b7
--- /dev/null
+++ b/tests/topotests/bgp_basic_functionality_topo1/bgp_basic_functionality.json
@@ -0,0 +1,115 @@
+{
+ "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": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}},
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {}}},
+ "r3": {"dest_link": {"r1": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {}}},
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {}}},
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {}}},
+ "r2": {"dest_link": {"r3": {}}},
+ "r4": {"dest_link": {"r3": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {}}},
+ "r2": {"dest_link": {"r3": {}}},
+ "r4": {"dest_link": {"r3": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}}
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
new file mode 100644
index 0000000..b18e32f
--- /dev/null
+++ b/tests/topotests/bgp_basic_functionality_topo1/test_bgp_basic_functionality.py
@@ -0,0 +1,1182 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 BGP basic functionality:
+
+Test steps
+- Create topology (setup module)
+ Creating 4 routers topology, r1, r2, r3 are in IBGP and
+ r3, r4 are in EBGP
+- Bring up topology
+- Verify for bgp to converge
+- Modify/Delete and verify router-id
+- Modify and verify bgp timers
+- Create and verify static routes
+- Modify and verify admin distance for existing static routes
+- Test advertise network using network command
+- Verify clear bgp
+- Test bgp convergence with loopback interface
+- Test advertise network using network command
+- Verify routes not installed in zebra when /32 routes received
+ with loopback BGP session subnet
+"""
+# XXX clean up in later commit to avoid conflict on rebase
+# pylint: disable=C0413
+
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+
+# 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.
+
+from lib.bgp import (
+ clear_bgp_and_verify,
+ create_router_bgp,
+ modify_as_number,
+ verify_as_numbers,
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ verify_bgp_timers_and_functionality,
+ verify_router_id,
+)
+from lib.common_config import (
+ addKernelRoute,
+ apply_raw_config,
+ check_address_types,
+ create_prefix_lists,
+ create_route_maps,
+ create_static_routes,
+ required_linux_kernel_version,
+ reset_config_on_routers,
+ start_topology,
+ step,
+ verify_admin_distance_for_static_routes,
+ verify_bgp_community,
+ verify_fib_routes,
+ verify_rib,
+ write_test_footer,
+ write_test_header,
+)
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global Variable
+KEEPALIVETIMER = 2
+HOLDDOWNTIMER = 6
+r1_ipv4_loopback = "1.0.1.0/24"
+r2_ipv4_loopback = "1.0.2.0/24"
+r3_ipv4_loopback = "1.0.3.0/24"
+r4_ipv4_loopback = "1.0.4.0/24"
+r1_ipv6_loopback = "2001:db8:f::1:0/120"
+r2_ipv6_loopback = "2001:db8:f::2:0/120"
+r3_ipv6_loopback = "2001:db8:f::3:0/120"
+r4_ipv6_loopback = "2001:db8:f::4:0/120"
+NETWORK = {
+ "ipv4": ["100.1.1.1/32", "100.1.1.2/32"],
+ "ipv6": ["100::1/128", "100::2/128"],
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_basic_functionality.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ 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("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 test_modify_and_delete_router_id(request):
+ """Test to modify, delete and verify router-id."""
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Modify router id
+ input_dict = {
+ "r1": {"bgp": {"router_id": "12.12.12.12"}},
+ "r2": {"bgp": {"router_id": "22.22.22.22"}},
+ "r3": {"bgp": {"router_id": "33.33.33.33"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying router id once modified
+ result = verify_router_id(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Delete router id
+ input_dict = {
+ "r1": {"bgp": {"del_router_id": True}},
+ "r2": {"bgp": {"del_router_id": True}},
+ "r3": {"bgp": {"del_router_id": True}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying router id once deleted
+ # Once router-id is deleted, highest interface ip should become
+ # router-id
+ result = verify_router_id(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_config_with_4byte_as_number(request):
+ """
+ Configure BGP with 4 byte ASN and verify it works fine
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r1": {"bgp": {"local_as": 131079}},
+ "r2": {"bgp": {"local_as": 131079}},
+ "r3": {"bgp": {"local_as": 131079}},
+ "r4": {"bgp": {"local_as": 131080}},
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_as_numbers(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_config_with_invalid_ASN_p2(request):
+ """
+ Configure BGP with invalid ASN(ex - 0, reserved ASN) and verify test case
+ ended up with error
+ """
+
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to modify AS number
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "local_as": 0,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 0,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 0,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 64000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ assert (
+ result is not True
+ ), "Expected BGP config is not created because of invalid ASNs: {}".format(result)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ result = verify_bgp_convergence(tgen, topo)
+ if result != True:
+ assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_config_with_2byteAS_and_4byteAS_number_p1(request):
+ """
+ Configure BGP with 4 byte and 2 byte ASN and verify BGP is converged
+ """
+
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ result = verify_bgp_convergence(tgen, topo)
+ if result != True:
+ assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
+
+ # Api call to modify AS number
+ input_dict = {
+ "r1": {"bgp": {"local_as": 131079}},
+ "r2": {"bgp": {"local_as": 131079}},
+ "r3": {"bgp": {"local_as": 131079}},
+ "r4": {"bgp": {"local_as": 111}},
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ if result != True:
+ assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
+
+ result = verify_as_numbers(tgen, topo, input_dict)
+ if result != True:
+ assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
+
+ # Api call verify whether BGP is converged
+ result = verify_bgp_convergence(tgen, topo)
+ if result != True:
+ assert False, "Testcase " + tc_name + " :Failed \n Error: {}".format(result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_timers_functionality(request):
+ """
+ Test to modify bgp timers and verify timers functionality.
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to modify BGP timerse
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, deepcopy(input_dict))
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so timer modification would take place
+ clear_bgp_and_verify(tgen, topo, "r1")
+
+ # Verifying bgp timers functionality
+ result = verify_bgp_timers_and_functionality(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_static_routes(request):
+ """Test to create and verify static routes."""
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to create static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "10.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.2",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ next_hop = ["10.0.0.2", "10.0.0.5"]
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, next_hop=next_hop, protocol=protocol
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_admin_distance_for_existing_static_routes(request):
+ """Test to modify and verify admin distance for existing static routes."""
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "10.0.20.1/32",
+ "admin_distance": 10,
+ "next_hop": "10.0.0.2",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying admin distance once modified
+ result = verify_admin_distance_for_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_advertise_network_using_network_command(request):
+ """Test advertise networks using network command."""
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "20.0.0.0/32", "no_of_network": 10},
+ {"network": "30.0.0.0/32", "no_of_network": 10},
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r2"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_clear_bgp_and_verify(request):
+ """
+ Created few static routes and verified all routes are learned via BGP
+ cleared BGP and verified all routes are intact
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # clear ip bgp
+ result = clear_bgp_and_verify(tgen, topo, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_attributes_with_vrf_default_keyword_p0(request):
+ """
+ TC_9:
+ Verify BGP functionality for default vrf with
+ "vrf default" keyword.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure static routes and redistribute in BGP on R3")
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "no_of_ip": 4,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r3": {
+ "bgp": {
+ "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(
+ "Create a route-map to match a specific prefix and modify"
+ "BGP attributes for matched prefix"
+ )
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "ABC": [
+ {
+ "seqid": 10,
+ "action": "permit",
+ "network": NETWORK["ipv4"][0],
+ }
+ ]
+ },
+ "ipv6": {
+ "XYZ": [
+ {
+ "seqid": 100,
+ "action": "permit",
+ "network": NETWORK["ipv6"][0],
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ if addr_type == "ipv4":
+ pf_list = "ABC"
+ else:
+ pf_list = "XYZ"
+
+ input_dict_6 = {
+ "r3": {
+ "route_maps": {
+ "BGP_ATTR_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {addr_type: {"prefix_lists": pf_list}},
+ "set": {
+ "aspath": {"as_num": 500, "as_action": "prepend"},
+ "localpref": 500,
+ "origin": "egp",
+ "community": {"num": "500:500", "action": "additive"},
+ "large_community": {
+ "num": "500:500:500",
+ "action": "additive",
+ },
+ },
+ },
+ {"action": "permit", "seq_id": 20},
+ ]
+ },
+ "BGP_ATTR_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 100,
+ "match": {addr_type: {"prefix_lists": pf_list}},
+ "set": {
+ "aspath": {"as_num": 500, "as_action": "prepend"},
+ "localpref": 500,
+ "origin": "egp",
+ "community": {"num": "500:500", "action": "additive"},
+ "large_community": {
+ "num": "500:500:500",
+ "action": "additive",
+ },
+ },
+ },
+ {"action": "permit", "seq_id": 200},
+ ],
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply the route-map on R3 in outbound direction for peer R4")
+
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "BGP_ATTR_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "BGP_ATTR_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify modified attributes for specific prefix with 'vrf default'"
+ "keyword on R4"
+ )
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "vrf": "default",
+ "largeCommunity": "500:500:500",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "vrf": "default",
+ "community": "500:500",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_4 = {"largeCommunity": "500:500:500", "community": "500:500"}
+
+ result = verify_bgp_community(
+ tgen, addr_type, dut, [NETWORK[addr_type][0]], input_dict_4
+ )
+ assert result is True, "Test case {} : Should fail \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_with_loopback_interface(request):
+ """
+ Test BGP with loopback interface
+
+ Adding keys:value pair "dest_link": "lo" and "source_link": "lo"
+ peer dict of input json file for all router's creating config using
+ loopback interface. Once BGP neighboship is up then verifying BGP
+ convergence
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for routerN in sorted(topo["routers"].keys()):
+ for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"]["ipv4"][
+ "unicast"
+ ]["neighbor"].keys():
+
+ # Adding ['source_link'] = 'lo' key:value pair
+ topo["routers"][routerN]["bgp"]["address_family"]["ipv4"]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {
+ "lo": {
+ "source_link": "lo",
+ }
+ }
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "1.0.2.17/32", "next_hop": "10.0.0.2"},
+ {"network": "1.0.3.17/32", "next_hop": "10.0.0.6"},
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {"network": "1.0.1.17/32", "next_hop": "10.0.0.1"},
+ {"network": "1.0.3.17/32", "next_hop": "10.0.0.10"},
+ ]
+ },
+ "r3": {
+ "static_routes": [
+ {"network": "1.0.1.17/32", "next_hop": "10.0.0.5"},
+ {"network": "1.0.2.17/32", "next_hop": "10.0.0.9"},
+ {"network": "1.0.4.17/32", "next_hop": "10.0.0.14"},
+ ]
+ },
+ "r4": {"static_routes": [{"network": "1.0.3.17/32", "next_hop": "10.0.0.13"}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether BGP is converged
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_with_loopback_with_same_subnet_p1(request):
+ """
+ Verify routes not installed in zebra when /32 routes received
+ with loopback BGP session subnet
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+ step("Delete BGP seesion created initially")
+ input_dict_r1 = {
+ "r1": {"bgp": {"delete": True}},
+ "r2": {"bgp": {"delete": True}},
+ "r3": {"bgp": {"delete": True}},
+ "r4": {"bgp": {"delete": True}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create BGP session over loop address")
+ topo_modify = deepcopy(topo)
+
+ for routerN in sorted(topo["routers"].keys()):
+ for addr_type in ADDR_TYPES:
+ for bgp_neighbor in topo_modify["routers"][routerN]["bgp"][
+ "address_family"
+ ][addr_type]["unicast"]["neighbor"].keys():
+
+ # Adding ['source_link'] = 'lo' key:value pair
+ topo_modify["routers"][routerN]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][bgp_neighbor]["dest_link"] = {
+ "lo": {"source_link": "lo", "ebgp_multihop": 2}
+ }
+
+ result = create_router_bgp(tgen, topo_modify["routers"])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Disable IPv6 BGP nbr from ipv4 address family")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]),
+ "address-family ipv4 unicast",
+ "no neighbor {} activate".format(
+ topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ "no neighbor {} activate".format(
+ topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]),
+ "address-family ipv4 unicast",
+ "no neighbor {} activate".format(
+ topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ "no neighbor {} activate".format(
+ topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]),
+ "address-family ipv4 unicast",
+ "no neighbor {} activate".format(
+ topo["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ "no neighbor {} activate".format(
+ topo["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ "no neighbor {} activate".format(
+ topo["routers"]["r4"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ "r4": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r4"]["bgp"]["local_as"]),
+ "address-family ipv4 unicast",
+ "no neighbor {} activate".format(
+ topo["routers"]["r3"]["links"]["lo"]["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ }
+
+ step("Configure kernel routes")
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ r1_ipv4_lo = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_ipv6_lo = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r2_ipv4_lo = topo["routers"]["r2"]["links"]["lo"]["ipv4"]
+ r2_ipv6_lo = topo["routers"]["r2"]["links"]["lo"]["ipv6"]
+ r3_ipv4_lo = topo["routers"]["r3"]["links"]["lo"]["ipv4"]
+ r3_ipv6_lo = topo["routers"]["r3"]["links"]["lo"]["ipv6"]
+ r4_ipv4_lo = topo["routers"]["r4"]["links"]["lo"]["ipv4"]
+ r4_ipv6_lo = topo["routers"]["r4"]["links"]["lo"]["ipv6"]
+
+ r1_r2 = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ r2_r1 = topo["routers"]["r2"]["links"]["r1"]["ipv6"].split("/")[0]
+ r1_r3 = topo["routers"]["r1"]["links"]["r3"]["ipv6"].split("/")[0]
+ r3_r1 = topo["routers"]["r3"]["links"]["r1"]["ipv6"].split("/")[0]
+ r2_r3 = topo["routers"]["r2"]["links"]["r3"]["ipv6"].split("/")[0]
+ r3_r2 = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ r3_r4 = topo["routers"]["r3"]["links"]["r4"]["ipv6"].split("/")[0]
+ r4_r3 = topo["routers"]["r4"]["links"]["r3"]["ipv6"].split("/")[0]
+
+ r1_r2_ipv4 = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ r2_r1_ipv4 = topo["routers"]["r2"]["links"]["r1"]["ipv4"].split("/")[0]
+ r1_r3_ipv4 = topo["routers"]["r1"]["links"]["r3"]["ipv4"].split("/")[0]
+ r3_r1_ipv4 = topo["routers"]["r3"]["links"]["r1"]["ipv4"].split("/")[0]
+ r2_r3_ipv4 = topo["routers"]["r2"]["links"]["r3"]["ipv4"].split("/")[0]
+ r3_r2_ipv4 = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ r3_r4_ipv4 = topo["routers"]["r3"]["links"]["r4"]["ipv4"].split("/")[0]
+ r4_r3_ipv4 = topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0]
+
+ r1_r2_intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ r2_r1_intf = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ r1_r3_intf = topo["routers"]["r1"]["links"]["r3"]["interface"]
+ r3_r1_intf = topo["routers"]["r3"]["links"]["r1"]["interface"]
+ r2_r3_intf = topo["routers"]["r2"]["links"]["r3"]["interface"]
+ r3_r2_intf = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ r3_r4_intf = topo["routers"]["r3"]["links"]["r4"]["interface"]
+ r4_r3_intf = topo["routers"]["r4"]["links"]["r3"]["interface"]
+
+ ipv4_list = [
+ ("r1", r1_r2_intf, r2_ipv4_loopback),
+ ("r1", r1_r3_intf, r3_ipv4_loopback),
+ ("r2", r2_r1_intf, r1_ipv4_loopback),
+ ("r2", r2_r3_intf, r3_ipv4_loopback),
+ ("r3", r3_r1_intf, r1_ipv4_loopback),
+ ("r3", r3_r2_intf, r2_ipv4_loopback),
+ ("r3", r3_r4_intf, r4_ipv4_loopback),
+ ("r4", r4_r3_intf, r3_ipv4_loopback),
+ ]
+
+ ipv6_list = [
+ ("r1", r1_r2_intf, r2_ipv6_loopback, r2_r1),
+ ("r1", r1_r3_intf, r3_ipv6_loopback, r3_r1),
+ ("r2", r2_r1_intf, r1_ipv6_loopback, r1_r2),
+ ("r2", r2_r3_intf, r3_ipv6_loopback, r3_r2),
+ ("r3", r3_r1_intf, r1_ipv6_loopback, r1_r3),
+ ("r3", r3_r2_intf, r2_ipv6_loopback, r2_r3),
+ ("r3", r3_r4_intf, r4_ipv6_loopback, r4_r3),
+ ("r4", r4_r3_intf, r3_ipv6_loopback, r3_r4),
+ ]
+
+ for dut, intf, loop_addr in ipv4_list:
+ result = addKernelRoute(tgen, dut, intf, loop_addr)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ for dut, intf, loop_addr, next_hop in ipv6_list:
+ result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static routes")
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": r2_ipv4_loopback, "next_hop": r2_r1_ipv4},
+ {"network": r3_ipv4_loopback, "next_hop": r3_r1_ipv4},
+ {"network": r2_ipv6_loopback, "next_hop": r2_r1},
+ {"network": r3_ipv6_loopback, "next_hop": r3_r1},
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {"network": r1_ipv4_loopback, "next_hop": r1_r2_ipv4},
+ {"network": r3_ipv4_loopback, "next_hop": r3_r2_ipv4},
+ {"network": r1_ipv6_loopback, "next_hop": r1_r2},
+ {"network": r3_ipv6_loopback, "next_hop": r3_r2},
+ ]
+ },
+ "r3": {
+ "static_routes": [
+ {"network": r1_ipv4_loopback, "next_hop": r1_r3_ipv4},
+ {"network": r2_ipv4_loopback, "next_hop": r2_r3_ipv4},
+ {"network": r4_ipv4_loopback, "next_hop": r4_r3_ipv4},
+ {"network": r1_ipv6_loopback, "next_hop": r1_r3},
+ {"network": r2_ipv6_loopback, "next_hop": r2_r3},
+ {"network": r4_ipv6_loopback, "next_hop": r4_r3},
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {"network": r3_ipv4_loopback, "next_hop": r3_r4_ipv4},
+ {"network": r3_ipv6_loopback, "next_hop": r3_r4},
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP session convergence")
+
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure redistribute connected on R2 and R4")
+ input_dict_1 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify Ipv4 and Ipv6 network installed in R1 RIB but not in FIB")
+ input_dict_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": "1.0.2.17/32"},
+ {"network": "2001:db8:f::2:17/128"},
+ ]
+ }
+ }
+
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_fib_routes(
+ tgen, addr_type, dut, input_dict_r1, expected=False
+ ) # pylint: disable=E1123
+ assert result is not True, (
+ "Testcase {} : Failed \n".format(tc_name)
+ + "Expected behavior: routes should not present in fib \n"
+ + "Error: {}".format(result)
+ )
+
+ step("Verify Ipv4 and Ipv6 network installed in r3 RIB but not in FIB")
+ input_dict_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": "1.0.4.17/32"},
+ {"network": "2001:db8:f::4:17/128"},
+ ]
+ }
+ }
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_fib_routes(
+ tgen, addr_type, dut, input_dict_r1, expected=False
+ ) # pylint: disable=E1123
+ assert result is not True, (
+ "Testcase {} : Failed \n".format(tc_name)
+ + "Expected behavior: routes should not present in fib \n"
+ + "Error: {}".format(result)
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/__init__.py b/tests/topotests/bgp_bfd_down_cease_notification/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/__init__.py
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf
new file mode 100644
index 0000000..0ae384e
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ peer 192.168.255.2 interface r1-eth0
+ exit
+ !
+exit
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf
new file mode 100644
index 0000000..2071c25
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as external
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 bfd
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf b/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf
new file mode 100644
index 0000000..091794f
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf
new file mode 100644
index 0000000..dcd5033
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ peer 192.168.255.1 interface r2-eth0
+ exit
+ !
+exit
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf
new file mode 100644
index 0000000..3279804
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as external
+ neighbor 192.168.255.1 timers 3 10
+ neighbor 192.168.255.1 bfd
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf b/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf
new file mode 100644
index 0000000..1fcccbe
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.2/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py
new file mode 100644
index 0000000..a9b43e2
--- /dev/null
+++ b/tests/topotests/bgp_bfd_down_cease_notification/test_bgp_bfd_down_cease_notification.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python
+
+#
+# bgp_bfd_down_cease_notification.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+Check if Cease/BFD Down notification message is sent/received
+when the BFD is down.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import kill_router_daemons, step
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BFD, os.path.join(CWD, "{}/bfdd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_bfd_down_notification():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ "peerBfdInfo": {"status": "Up"},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_bfd_down_notification():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "lastNotificationReason": "Cease/BFD Down",
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Kill bfdd on R2")
+ kill_router_daemons(tgen, "r2", ["bfdd"])
+
+ step("Check if we received Cease/BFD Down notification message")
+ test_func = functools.partial(_bgp_bfd_down_notification)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP Cease/BFD Down notification message on R2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_blackhole_community/__init__.py b/tests/topotests/bgp_blackhole_community/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/__init__.py
diff --git a/tests/topotests/bgp_blackhole_community/r1/bgpd.conf b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf
new file mode 100644
index 0000000..574d199
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r1/bgpd.conf
@@ -0,0 +1,14 @@
+!
+router bgp 65001
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ neighbor r1-eth0 interface remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor r1-eth0 route-map r2 out
+ exit-address-family
+ !
+!
+route-map r2 permit 10
+ set community blackhole
+!
diff --git a/tests/topotests/bgp_blackhole_community/r1/zebra.conf b/tests/topotests/bgp_blackhole_community/r1/zebra.conf
new file mode 100644
index 0000000..70dc5e5
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r1/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+ip forwarding
+!
+
diff --git a/tests/topotests/bgp_blackhole_community/r2/bgpd.conf b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf
new file mode 100644
index 0000000..2260613
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r2/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor r2-eth0 interface remote-as external
+ neighbor r2-eth1 interface remote-as external
+ neighbor r2-eth2 interface remote-as internal
+!
diff --git a/tests/topotests/bgp_blackhole_community/r2/zebra.conf b/tests/topotests/bgp_blackhole_community/r2/zebra.conf
new file mode 100644
index 0000000..cf6fb6d
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r2/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface r2-eth0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1
+ ip address 192.168.1.1/24
+!
+interface r2-eth2
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_blackhole_community/r3/bgpd.conf b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf
new file mode 100644
index 0000000..d0c4b40
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r3/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65003
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ neighbor r3-eth0 interface remote-as external
+!
diff --git a/tests/topotests/bgp_blackhole_community/r3/zebra.conf b/tests/topotests/bgp_blackhole_community/r3/zebra.conf
new file mode 100644
index 0000000..05ab56d
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.1.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_blackhole_community/r4/bgpd.conf b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf
new file mode 100644
index 0000000..0ac963e
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r4/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65002
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ neighbor r4-eth0 interface remote-as internal
+!
diff --git a/tests/topotests/bgp_blackhole_community/r4/zebra.conf b/tests/topotests/bgp_blackhole_community/r4/zebra.conf
new file mode 100644
index 0000000..e2ccaed
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
new file mode 100644
index 0000000..4f8fc0d
--- /dev/null
+++ b/tests/topotests/bgp_blackhole_community/test_bgp_blackhole_community.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if 172.16.255.254/32 tagged with BLACKHOLE community is not
+re-advertised downstream outside local AS.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_blackhole_community():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ expected = {"paths": [{"community": {"list": ["blackhole", "noExport"]}}]}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_no_advertise_ebgp():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd(
+ "show ip bgp neighbor r2-eth1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {},
+ "totalPrefixCounter": 0,
+ "filteredPrefixCounter": 0,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_no_advertise_ibgp():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd(
+ "show ip bgp neighbor r2-eth2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {"172.16.255.254/32": {}},
+ "totalPrefixCounter": 2,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r2"])
+
+ step("Check if 172.16.255.254/32 is not advertised to eBGP peers")
+
+ test_func = functools.partial(_bgp_no_advertise_ebgp)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Advertised blackhole tagged prefix to eBGP peers in "{}"'.format(
+ tgen.gears["r2"]
+ )
+
+ step("Check if 172.16.255.254/32 is advertised to iBGP peers")
+
+ test_func = functools.partial(_bgp_no_advertise_ibgp)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Withdrawn blackhole tagged prefix to iBGP peers in "{}"'.format(
+ tgen.gears["r2"]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_comm_list_delete/__init__.py b/tests/topotests/bgp_comm_list_delete/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/__init__.py
diff --git a/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf b/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf
new file mode 100644
index 0000000..12161d2
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/r1/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected route-map r2-out
+ exit-address-family
+!
+route-map r2-out permit 10
+ set community 111:111 222:222 333:333 444:444
+!
diff --git a/tests/topotests/bgp_comm_list_delete/r1/zebra.conf b/tests/topotests/bgp_comm_list_delete/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf b/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf
new file mode 100644
index 0000000..33231b5
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/r2/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4
+ neighbor 192.168.255.1 route-map r1-in in
+ exit-address-family
+!
+bgp community-list standard r1 permit 333:333
+!
+route-map r1-in permit 10
+ set comm-list r1 delete
+!
diff --git a/tests/topotests/bgp_comm_list_delete/r2/zebra.conf b/tests/topotests/bgp_comm_list_delete/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py b/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py
new file mode 100644
index 0000000..4db4e37
--- /dev/null
+++ b/tests/topotests/bgp_comm_list_delete/test_bgp_comm-list_delete.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+#
+# bgp_comm-list_delete.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+bgp_comm-list_delete.py:
+
+Test if works the following commands:
+route-map test permit 10
+ set comm-list <arg> delete
+"""
+
+import os
+import sys
+import json
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_invalid():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ while True:
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
+ if output["192.168.255.1"]["bgpState"] == "Established":
+ if (
+ output["192.168.255.1"]["addressFamilyInfo"]["ipv4Unicast"][
+ "acceptedPrefixCounter"
+ ]
+ == 2
+ ):
+ return True
+
+ def _bgp_comm_list_delete(router):
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ if "333:333" in output["paths"][0]["community"]["list"]:
+ return False
+ return True
+
+ if _bgp_converge("r2"):
+ assert _bgp_comm_list_delete("r2") == True
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_communities_topo1/bgp_communities.json b/tests/topotests/bgp_communities_topo1/bgp_communities.json
new file mode 100644
index 0000000..da6aec2
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/bgp_communities.json
@@ -0,0 +1,175 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json b/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json
new file mode 100644
index 0000000..fa89f6b
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/bgp_communities_topo2.json
@@ -0,0 +1,191 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_communities_topo1/test_bgp_communities.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py
new file mode 100644
index 0000000..e4c25ff
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities.py
@@ -0,0 +1,643 @@
+#!/usr/bin/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 bgp community functionality:
+- Verify routes are not advertised when NO-ADVERTISE Community is applied
+
+"""
+
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ create_route_maps,
+ create_prefix_lists,
+ create_route_maps,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+from copy import deepcopy
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_communities.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_bgp_no_advertise_community_p0(request):
+ """
+ Verify routes are not advertised when NO-ADVERTISE Community is applied
+
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ NEXT_HOP_IP = {
+ "ipv4": topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0],
+ }
+
+ # configure static routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static and connected in Router BGP " "in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "BGP neighbors are up, static and connected route advertised from"
+ " R1 are present on R2 BGP table and RIB using show ip bgp and "
+ " show ip route"
+ )
+ step(
+ "Static and connected route advertised from R1 are present on R3"
+ " BGP table and RIB using show ip bgp and show ip route"
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure prefix list P1 on R2 to permit route coming from R1")
+ # Create ip prefix list
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_1_{}".format(addr_type): [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {"prefix_lists": "pf_list_1_" + addr_type}
+ },
+ "set": {"community": {"num": "no-advertise"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no"
+ " advertise community"
+ )
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_"
+ + addr_type,
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After advertising no advertise community to BGP neighbor "
+ "static and connected router got removed from R3 verify using "
+ "show ip bgp & show ip route"
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n ".format(
+ tc_name
+ ) + " Routes still present in R3 router. Error: {}".format(result)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove and Add no advertise community")
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_"
+ + addr_type,
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing no advertise community from BGP neighbor "
+ "static and connected router got advertised to R3 and "
+ "removing route-map, verify route using show ip bgp"
+ " and show ip route"
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Repeat above steps when IBGP nbr configured between R1, R2 & R2, R3")
+ topo1 = deepcopy(topo)
+
+ topo1["routers"]["r1"]["bgp"]["local_as"] = "100"
+ topo1["routers"]["r2"]["bgp"]["local_as"] = "100"
+ topo1["routers"]["r3"]["bgp"]["local_as"] = "100"
+
+ for rtr in ["r1", "r2", "r3"]:
+ if "bgp" in topo1["routers"][rtr].keys():
+ delete_bgp = {rtr: {"bgp": {"delete": True}}}
+ result = create_router_bgp(tgen, topo1, delete_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ config_bgp = {
+ rtr: {"bgp": {"local_as": topo1["routers"][rtr]["bgp"]["local_as"]}}
+ }
+ result = create_router_bgp(tgen, topo1, config_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ step("verify bgp convergence before starting test case")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo1)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ # configure static routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute static and connected in Router " "BGP in R1")
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "BGP neighbors are up, static and connected route advertised from"
+ " R1 are present on R2 BGP table and RIB using show ip bgp and "
+ " show ip route"
+ )
+ step(
+ "Static and connected route advertised from R1 are present on R3"
+ " BGP table and RIB using show ip bgp and show ip route"
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure prefix list P1 on R2 to permit route coming from R1")
+ # Create ip prefix list
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_1_{}".format(addr_type): [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {"prefix_lists": "pf_list_1_" + addr_type}
+ },
+ "set": {"community": {"num": "no-advertise"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Apply route-map RM1 on R2, R2 to R3 BGP neighbor with no"
+ " advertise community"
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_"
+ + addr_type,
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After advertising no advertise community to BGP neighbor "
+ "static and connected router got removed from R3 verify using "
+ "show ip bgp & show ip route"
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove and Add no advertise community")
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_"
+ + addr_type,
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing no advertise community from BGP neighbor "
+ "static and connected router got advertised to R3 and "
+ "removing route verify using show ip bgp and "
+ " show ip route"
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes still present in R3 router. 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/bgp_communities_topo1/test_bgp_communities_topo2.py b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py
new file mode 100644
index 0000000..f6ee9ea
--- /dev/null
+++ b/tests/topotests/bgp_communities_topo1/test_bgp_communities_topo2.py
@@ -0,0 +1,404 @@
+#!/usr/bin/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 bgp community functionality:
+1. Verify that BGP well known communities work fine for
+ eBGP and iBGP peers.
+ Well known communities tested: no-export, local-AS, internet
+
+"""
+
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ create_route_maps,
+ create_route_maps,
+ required_linux_kernel_version,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_community,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": ["192.0.2.1/32", "192.0.2.2/32"],
+ "ipv6": ["2001:DB8::1:1/128", "2001:DB8::1:2/128"],
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.14")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_communities_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_bgp_no_export_local_as_and_internet_communities_p0(request):
+ """
+ Verify that BGP well known communities work fine for
+ eBGP and iBGP peers.
+ Well known communities tested: no-export, local-AS, internet
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config: Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step("Configure static routes on R1 with next-hop as null0")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for comm_type in ["no-export", "local-AS", "internet"]:
+
+ step("Create a route-map on R1 to set community as {}".format(comm_type))
+
+ seq_id = 10
+ input_rmap = {
+ "r1": {
+ "route_maps": {
+ "rmap_wkc": [
+ {
+ "action": "permit",
+ "seq_id": seq_id,
+ "set": {"community": {"num": "{}".format(comm_type)}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_rmap)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply route-map while redistributing static routes into BGP")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"route-map": "rmap_wkc"},
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"route-map": "rmap_wkc"},
+ }
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+
+ step("Verify that BGP prefixes on R1 have community: {}".format(comm_type))
+ input_dict_4 = {"community": "{}".format(comm_type)}
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, addr_type, "r1", NETWORK[addr_type], input_dict_4
+ )
+ assert result is True, "Test case {} : Should fail \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r1"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ if comm_type == "internet":
+ step(
+ "Verify that these prefixes, originated on R1, are"
+ "received on both R2 and R3"
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_4,
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[
+ 0
+ ],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ step(
+ "Verify that these prefixes, originated on R1, are not"
+ "received on R3 but received on R2"
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_4,
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[
+ 0
+ ],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove route-map from redistribute static on R1")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "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 that these prefixes, originated on R1, are now"
+ "received on both routers R2 and R3"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r1"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_4,
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
+ )
+ 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/bgp_community_alias/__init__.py b/tests/topotests/bgp_community_alias/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/__init__.py
diff --git a/tests/topotests/bgp_community_alias/r1/bgpd.conf b/tests/topotests/bgp_community_alias/r1/bgpd.conf
new file mode 100644
index 0000000..844ab28
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/r1/bgpd.conf
@@ -0,0 +1,26 @@
+!
+bgp send-extra-data zebra
+!
+bgp community alias 65001:1 community-r2-1
+bgp community alias 65002:2 community-r2-2
+bgp community alias 65001:1:1 large-community-r2-1
+!
+bgp large-community-list expanded r2 seq 5 permit _65001:1:1_
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+route-map r2 permit 10
+ match alias community-r2-1
+ set tag 10
+route-map r2 permit 20
+ match alias community-r2-2
+ set tag 20
+route-map r2 permit 30
+ set tag 100
+!
diff --git a/tests/topotests/bgp_community_alias/r1/zebra.conf b/tests/topotests/bgp_community_alias/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_community_alias/r2/bgpd.conf b/tests/topotests/bgp_community_alias/r2/bgpd.conf
new file mode 100644
index 0000000..7806077
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/r2/bgpd.conf
@@ -0,0 +1,25 @@
+!
+bgp send-extra-data zebra
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.1 route-map r1 out
+ exit-address-family
+!
+ip prefix-list p1 permit 172.16.16.1/32
+ip prefix-list p2 permit 172.16.16.2/32
+ip prefix-list p3 permit 172.16.16.3/32
+!
+route-map r1 permit 10
+ match ip address prefix-list p1
+ set community 65001:1 65001:2
+ set large-community 65001:1:1 65001:1:2
+route-map r1 permit 20
+ match ip address prefix-list p2
+ set community 65002:1 65002:2
+route-map r1 permit 30
+ match ip address prefix-list p3
+!
diff --git a/tests/topotests/bgp_community_alias/r2/zebra.conf b/tests/topotests/bgp_community_alias/r2/zebra.conf
new file mode 100644
index 0000000..b8cb9ba
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+int lo
+ ip address 172.16.16.1/32
+ ip address 172.16.16.2/32
+ ip address 172.16.16.3/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_community_alias/test_bgp-community-alias.py b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
new file mode 100644
index 0000000..0b41dc7
--- /dev/null
+++ b/tests/topotests/bgp_community_alias/test_bgp-community-alias.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if BGP community alias is visible in CLI outputs
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_community_alias():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "172.16.16.1/32": [
+ {
+ "tag": 10,
+ "communities": "community-r2-1 65001:2",
+ "largeCommunities": "large-community-r2-1 65001:1:2",
+ }
+ ],
+ "172.16.16.2/32": [
+ {
+ "tag": 20,
+ "communities": "65002:1 community-r2-2",
+ "largeCommunities": "",
+ }
+ ],
+ "172.16.16.3/32": [
+ {
+ "tag": 100,
+ "communities": "",
+ "largeCommunities": "",
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP community aliases at r1"
+
+ def _bgp_show_prefixes_by_alias(router):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast alias large-community-r2-1 json detail"
+ )
+ )
+ expected = {
+ "routes": {
+ "172.16.16.1/32": [
+ {
+ "community": {"string": "community-r2-1 65001:2"},
+ "largeCommunity": {"string": "large-community-r2-1 65001:1:2"},
+ }
+ ]
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_show_prefixes_by_alias, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP prefixes by community alias at r1"
+
+ def _bgp_show_prefixes_by_large_community_list(router):
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast large-community-list r2 json")
+ )
+ expected = {"routes": {"172.16.16.1/32": [{"valid": True}]}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_show_prefixes_by_large_community_list, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP prefixes by large community list at r1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_community_change_update/__init__.py b/tests/topotests/bgp_community_change_update/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/__init__.py
diff --git a/tests/topotests/bgp_community_change_update/c1/bgpd.conf b/tests/topotests/bgp_community_change_update/c1/bgpd.conf
new file mode 100644
index 0000000..24cf9df
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/c1/bgpd.conf
@@ -0,0 +1,11 @@
+!
+debug bgp updates
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.2 remote-as external
+ neighbor 10.0.1.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_community_change_update/c1/zebra.conf b/tests/topotests/bgp_community_change_update/c1/zebra.conf
new file mode 100644
index 0000000..e3dbbc0
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/c1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface c1-eth0
+ ip address 10.0.1.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
new file mode 100644
index 0000000..31f033a
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/test_bgp_community_change_update.py
@@ -0,0 +1,222 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+r"""
+Reference: https://www.cmand.org/communityexploration
+
+ --y2--
+ / | \
+ c1 ---- x1 ---- y1 | z1
+ \ | /
+ --y3--
+
+1. z1 announces 192.168.255.254/32 to y2, y3.
+2. y2 and y3 tags this prefix at ingress with appropriate
+communities 65004:2 (y2) and 65004:3 (y3).
+3. x1 filters all communities at the egress to c1.
+4. Shutdown the link between y1 and y2.
+5. y1 will generate a BGP UPDATE message regarding the next-hop change.
+6. x1 will generate a BGP UPDATE message regarding community change.
+
+To avoid sending duplicate BGP UPDATE messages we should make sure
+we send only actual route updates. In this example, x1 will skip
+BGP UPDATE to c1 because the actual route is the same
+(filtered communities - nothing changes).
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+from lib.common_config import step
+from time import sleep
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("z1")
+ tgen.add_router("y1")
+ tgen.add_router("y2")
+ tgen.add_router("y3")
+ tgen.add_router("x1")
+ tgen.add_router("c1")
+
+ # 10.0.1.0/30
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["c1"])
+ switch.add_link(tgen.gears["x1"])
+
+ # 10.0.2.0/30
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["x1"])
+ switch.add_link(tgen.gears["y1"])
+
+ # 10.0.3.0/30
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["y1"])
+ switch.add_link(tgen.gears["y2"])
+
+ # 10.0.4.0/30
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["y1"])
+ switch.add_link(tgen.gears["y3"])
+
+ # 10.0.5.0/30
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["y2"])
+ switch.add_link(tgen.gears["y3"])
+
+ # 10.0.6.0/30
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["y2"])
+ switch.add_link(tgen.gears["z1"])
+
+ # 10.0.7.0/30
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["y3"])
+ switch.add_link(tgen.gears["z1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_community_update_path_change():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge_initial():
+ output = json.loads(
+ tgen.gears["c1"].vtysh_cmd("show ip bgp neighbor 10.0.1.2 json")
+ )
+ expected = {
+ "10.0.1.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 8}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check if an initial topology is converged")
+ test_func = functools.partial(_bgp_converge_initial)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see bgp convergence in c1"
+
+ step("Disable link between y1 and y2")
+ tgen.gears["y1"].run("ip link set dev y1-eth1 down")
+
+ def _bgp_converge_link_disabled():
+ output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json"))
+ expected = {"10.0.3.2": {"bgpState": "Active"}}
+ return topotest.json_cmp(output, expected)
+
+ step("Check if a topology is converged after a link down between y1 and y2")
+ test_func = functools.partial(_bgp_converge_link_disabled)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see bgp convergence in y1"
+
+ def _bgp_check_for_duplicate_updates():
+ duplicate = False
+ i = 0
+ while i < 5:
+ if (
+ len(
+ tgen.gears["c1"].run(
+ 'grep "10.0.1.2(x1) rcvd 192.168.255.254/32 IPv4 unicast...duplicate ignored" bgpd.log'
+ )
+ )
+ > 0
+ ):
+ duplicate = True
+ i += 1
+ sleep(0.5)
+ return duplicate
+
+ step("Check if we see duplicate BGP UPDATE message in c1 (suppress-duplicates)")
+ assert (
+ _bgp_check_for_duplicate_updates() == False
+ ), "Seen duplicate BGP UPDATE message in c1 from x1"
+
+ step("Disable bgp suppress-duplicates at x1")
+ tgen.gears["x1"].run(
+ "vtysh -c 'conf' -c 'router bgp' -c 'no bgp suppress-duplicates'"
+ )
+
+ step("Enable link between y1 and y2")
+ tgen.gears["y1"].run("ip link set dev y1-eth1 up")
+
+ def _bgp_converge_link_enabled():
+ output = json.loads(tgen.gears["y1"].vtysh_cmd("show ip bgp nei 10.0.3.2 json"))
+ expected = {
+ "10.0.3.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {
+ "ipv4Unicast": {"acceptedPrefixCounter": 5, "sentPrefixCounter": 4}
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check if a topology is converged after a link up between y1 and y2")
+ test_func = functools.partial(_bgp_converge_link_enabled)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see bgp convergence in y1"
+
+ step(
+ "Check if we see duplicate BGP UPDATE message in c1 (no bgp suppress-duplicates)"
+ )
+ assert (
+ _bgp_check_for_duplicate_updates() == True
+ ), "Didn't see duplicate BGP UPDATE message in c1 from x1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_community_change_update/x1/bgpd.conf b/tests/topotests/bgp_community_change_update/x1/bgpd.conf
new file mode 100644
index 0000000..8d7bcb9
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/x1/bgpd.conf
@@ -0,0 +1,20 @@
+!
+debug bgp updates
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.1 remote-as external
+ neighbor 10.0.1.1 timers 3 10
+ neighbor 10.0.2.2 remote-as external
+ neighbor 10.0.2.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 10.0.1.1 route-map c1 out
+ exit-address-family
+!
+bgp community-list standard c1 seq 1 permit 65004:2
+bgp community-list standard c1 seq 2 permit 65004:3
+!
+route-map c1 permit 10
+ set comm-list c1 delete
+!
diff --git a/tests/topotests/bgp_community_change_update/x1/zebra.conf b/tests/topotests/bgp_community_change_update/x1/zebra.conf
new file mode 100644
index 0000000..8b7c03f
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/x1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface x1-eth0
+ ip address 10.0.1.2/30
+!
+interface x1-eth1
+ ip address 10.0.2.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_community_change_update/y1/bgpd.conf b/tests/topotests/bgp_community_change_update/y1/bgpd.conf
new file mode 100644
index 0000000..a010085
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.2.1 remote-as external
+ neighbor 10.0.2.1 timers 3 10
+ neighbor 10.0.3.2 remote-as internal
+ neighbor 10.0.3.2 timers 3 10
+ neighbor 10.0.4.2 remote-as internal
+ neighbor 10.0.4.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_community_change_update/y1/zebra.conf b/tests/topotests/bgp_community_change_update/y1/zebra.conf
new file mode 100644
index 0000000..4096f2a
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface y1-eth0
+ ip address 10.0.2.2/30
+!
+interface y1-eth1
+ ip address 10.0.3.1/30
+!
+interface y1-eth2
+ ip address 10.0.4.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_community_change_update/y2/bgpd.conf b/tests/topotests/bgp_community_change_update/y2/bgpd.conf
new file mode 100644
index 0000000..27f1e81
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.3.1 remote-as internal
+ neighbor 10.0.3.1 timers 3 10
+ neighbor 10.0.5.2 remote-as internal
+ neighbor 10.0.5.2 timers 3 10
+ neighbor 10.0.6.2 remote-as external
+ neighbor 10.0.6.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 10.0.3.1 route-reflector-client
+ neighbor 10.0.5.2 route-reflector-client
+ neighbor 10.0.6.2 route-map z1 in
+ exit-address-family
+!
+route-map z1 permit 10
+ set community 65004:2
+!
diff --git a/tests/topotests/bgp_community_change_update/y2/zebra.conf b/tests/topotests/bgp_community_change_update/y2/zebra.conf
new file mode 100644
index 0000000..7e9458a
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y2/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface y2-eth0
+ ip address 10.0.3.2/30
+!
+interface y2-eth1
+ ip address 10.0.5.1/30
+!
+interface y2-eth2
+ ip address 10.0.6.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_community_change_update/y3/bgpd.conf b/tests/topotests/bgp_community_change_update/y3/bgpd.conf
new file mode 100644
index 0000000..8e57284
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y3/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.4.1 remote-as internal
+ neighbor 10.0.4.1 timers 3 10
+ neighbor 10.0.5.1 remote-as internal
+ neighbor 10.0.5.1 timers 3 10
+ neighbor 10.0.7.2 remote-as external
+ neighbor 10.0.7.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 10.0.4.1 route-reflector-client
+ neighbor 10.0.5.1 route-reflector-client
+ neighbor 10.0.7.2 route-map z1 in
+ exit-address-family
+!
+route-map z1 permit 10
+ set community 65004:3
+!
diff --git a/tests/topotests/bgp_community_change_update/y3/zebra.conf b/tests/topotests/bgp_community_change_update/y3/zebra.conf
new file mode 100644
index 0000000..93dd87d
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/y3/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface y3-eth0
+ ip address 10.0.4.2/30
+!
+interface y3-eth1
+ ip address 10.0.5.2/30
+!
+interface y3-eth2
+ ip address 10.0.7.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_community_change_update/z1/bgpd.conf b/tests/topotests/bgp_community_change_update/z1/bgpd.conf
new file mode 100644
index 0000000..2f470f1
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/z1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65004
+ no bgp ebgp-requires-policy
+ neighbor 10.0.6.1 remote-as external
+ neighbor 10.0.6.1 timers 3 10
+ neighbor 10.0.7.1 remote-as external
+ neighbor 10.0.7.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_community_change_update/z1/zebra.conf b/tests/topotests/bgp_community_change_update/z1/zebra.conf
new file mode 100644
index 0000000..7f6bbb6
--- /dev/null
+++ b/tests/topotests/bgp_community_change_update/z1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 192.168.255.254/32
+!
+interface z1-eth0
+ ip address 10.0.6.2/30
+!
+interface z1-eth1
+ ip address 10.0.7.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/__init__.py b/tests/topotests/bgp_conditional_advertisement/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/__init__.py
diff --git a/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
new file mode 100644
index 0000000..293b38c
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r1/bgpd.conf
@@ -0,0 +1,31 @@
+!
+ip prefix-list CUST seq 5 permit 10.139.224.0/20
+ip prefix-list DEFAULT seq 5 permit 0.0.0.0/0
+ip prefix-list PL1 seq 5 permit 192.0.2.1/32
+!
+route-map CUST permit 10
+ match ip address prefix-list CUST
+ set community 64671:501
+!
+route-map RM1 permit 10
+ match ip address prefix-list PL1
+ set community 64952:3008
+!
+route-map DEF permit 10
+ match ip address prefix-list DEFAULT
+ set community 64848:3011 65011:200 65013:200
+!
+router bgp 1
+ bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
+ no bgp ebgp-requires-policy
+ neighbor 10.10.10.2 remote-as 2
+ !
+ address-family ipv4 unicast
+ network 0.0.0.0/0 route-map DEF
+ network 192.0.2.1/32 route-map RM1
+ network 192.0.2.5/32
+ redistribute connected route-map CUST
+ neighbor 10.10.10.2 soft-reconfiguration inbound
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf
new file mode 100644
index 0000000..bb887e4
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r1/zebra.conf
@@ -0,0 +1,19 @@
+!
+hostname Router1
+!
+ip route 0.0.0.0/0 blackhole
+ip route 192.0.2.1/32 blackhole
+ip route 192.0.2.2/32 blackhole
+ip route 192.0.2.3/32 blackhole
+ip route 192.0.2.4/32 blackhole
+ip route 192.0.2.5/32 blackhole
+!
+interface r1-eth0
+ ip address 10.10.10.1/24
+!
+interface lo
+ ip address 10.139.224.1/20
+!
+ip forwarding
+ipv6 forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
new file mode 100644
index 0000000..73f837c
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r2/bgpd.conf
@@ -0,0 +1,46 @@
+!
+ip prefix-list DEFAULT seq 5 permit 192.0.2.5/32
+ip prefix-list DEFAULT seq 10 permit 192.0.2.1/32
+ip prefix-list EXIST seq 5 permit 10.10.10.10/32
+ip prefix-list DEFAULT-ROUTE seq 5 permit 0.0.0.0/0
+ip prefix-list IP1 seq 5 permit 10.139.224.0/20
+ip prefix-list IP2 seq 5 permit 203.0.113.1/32
+!
+bgp community-list standard DC-ROUTES seq 5 permit 64952:3008
+bgp community-list standard DC-ROUTES seq 10 permit 64671:501
+bgp community-list standard DC-ROUTES seq 15 permit 64950:3009
+bgp community-list standard DEFAULT-ROUTE seq 5 permit 65013:200
+!
+route-map ADV-MAP-1 permit 10
+ match ip address prefix-list IP1
+!
+route-map ADV-MAP-1 permit 20
+ match community DC-ROUTES
+!
+route-map ADV-MAP-2 permit 10
+ match ip address prefix-list IP2
+ set metric 911
+!
+route-map EXIST-MAP permit 10
+ match community DEFAULT-ROUTE
+ match ip address prefix-list DEFAULT-ROUTE
+!
+route-map RMAP-1 deny 10
+ match ip address prefix-list IP1
+!
+route-map RMAP-2 deny 10
+ match ip address prefix-list IP2
+!
+router bgp 2
+ bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
+ no bgp ebgp-requires-policy
+ neighbor 10.10.10.1 remote-as 1
+ neighbor 10.10.20.3 remote-as 3
+ !
+ address-family ipv4 unicast
+ network 203.0.113.1/32
+ neighbor 10.10.10.1 soft-reconfiguration inbound
+ neighbor 10.10.20.3 soft-reconfiguration inbound
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf
new file mode 100644
index 0000000..434ab68
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r2/zebra.conf
@@ -0,0 +1,15 @@
+!
+hostname Router2
+!
+interface r2-eth0
+ ip address 10.10.10.2/24
+!
+interface r2-eth1
+ ip address 10.10.20.2/24
+!
+interface lo
+ ip address 203.0.113.1/32
+!
+ip forwarding
+ipv6 forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
new file mode 100644
index 0000000..f389f30
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r3/bgpd.conf
@@ -0,0 +1,12 @@
+!
+router bgp 3
+ bgp log-neighbor-changes
+ bgp conditional-advertisement timer 5
+ no bgp ebgp-requires-policy
+ neighbor 10.10.20.2 remote-as 2
+ !
+ address-family ipv4 unicast
+ neighbor 10.10.20.2 soft-reconfiguration inbound
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf
new file mode 100644
index 0000000..0dadfdb
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/r3/zebra.conf
@@ -0,0 +1,12 @@
+!
+hostname Router3
+!
+interface r3-eth0
+ ip address 10.10.20.3/24
+!
+interface lo
+ ip address 198.51.100.1/32
+!
+ip forwarding
+ipv6 forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
new file mode 100644
index 0000000..0e16b97
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement/test_bgp_conditional_advertisement.py
@@ -0,0 +1,1310 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_conditional_advertisement.py
+#
+# Copyright (c) 2020 by
+# Samsung R&D Institute India - Bangalore.
+# Madhurilatha Kuruganti
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+Test BGP conditional advertisement functionality.
+
+ +--------+ +--------+ +--------+
+ | | | | | |
+ | R1 |------------| R2 |------------| R3 |
+ | | | | | |
+ +--------+ +--------+ +--------+
+
+R2 is DUT and peers with R1 and R3 in default bgp instance.
+
+Following tests are covered under BGP conditional advertisement functionality.
+Conditional advertisement
+-------------------------
+TC11: R3 BGP convergence, without advertise-map configuration.
+ All routes are advertised to R3.
+
+TC21: exist-map routes present in R2's BGP table.
+ advertise-map routes present in R2's BGP table are advertised to R3.
+TC22: exist-map routes not present in R2's BGP table
+ advertise-map routes present in R2's BGP table are withdrawn from R3.
+TC23: advertise-map with exist-map configuration is removed from a peer
+ send normal BGP update to advertise previously withdrawn routes if any.
+
+TC31: non-exist-map routes not present in R2's BGP table
+ advertise-map routes present in R2's BGP table are advertised to R3.
+TC32: non-exist-map routes present in R2's BGP table
+ advertise-map routes present in R2's BGP table are withdrawn from R3.
+TC33: advertise-map with non-exist-map configuration is removed from a peer
+ send normal BGP update to advertisepreviously withdrawn routes if any.
+
+TC41: non-exist-map route-map configuration removed in R2.
+ advertise-map routes present in R2's BGP table are advertised to R3.
+TC42: exist-map route-map configuration removed in R2
+ advertise-map routes present in R2's BGP table are withdrawn from R3.
+
+Conditional advertisement(received routes) along with Route-map Filter
+----------------------------------------------------------------------
+TC51: exist-map routes present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 except advertise-map routes.
+TC52: exist-map routes present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 including advertise-map routes.
+TC53: non-exist-map routes present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 including advertise-map routes.
+TC54: non-exist-map routes present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 except advertise-map routes.
+
+TC61: exist-map routes not present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 including advertise-map routes.
+TC62: exist-map routes not present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 except advertise-map routes.
+TC63: non-exist-map routes not present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 except advertise-map routes.
+TC64: non-exist-map routes not present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 including advertise-map routes.
+
+Conditional advertisement(attached routes) along with Route-map Filter
+-----------------------------------------------------------------
+TC71: exist-map routes present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 except advertise-map routes.
+TC72: exist-map routes present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 including advertise-map routes.
+TC73: non-exist-map routes present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 including advertise-map routes.
+TC74: non-exist-map routes present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 except advertise-map routes.
+
+TC81: exist-map routes not present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 including advertise-map routes.
+TC82: exist-map routes not present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 except advertise-map routes.
+TC83: non-exist-map routes not present in R2's BGP table, with route-map filter.
+ All routes are withdrawn from R3 except advertise-map routes.
+TC84: non-exist-map routes not present in R2's BGP table, without route-map filter.
+ All routes are advertised to R3 including advertise-map routes.
+
+TC91: exist-map routes present in R2's BGP table, with route-map filter and network.
+ All routes are advertised to R3 including advertise-map routes.
+TC92: exist-map routes present in R2's BGP table, with route-map filter and no network.
+ All routes are advertised to R3 except advertise-map routes.
+TC93: non-exist-map routes not present in R2's BGP table, with route-map filter and network.
+ All routes are advertised to R3 including advertise-map routes.
+TC94: non-exist-map routes not present in R2's BGP table, with route-map filter and no network.
+ All routes are advertised to R3 except advertise-map routes.
+
+i.e.
++----------------+-------------------------+------------------------+
+| Routes in | exist-map status | advertise-map status |
+| BGP table | | |
++----------------+-------------------------+------------------------+
+| Present | Condition matched | Advertise |
++----------------+-------------------------+------------------------+
+| Not Present | Condition not matched | Withdrawn |
++----------------+-------------------------+------------------------+
+| | non-exist-map status | advertise-map status |
+| | | |
++----------------+-------------------------+------------------------+
+| Present | Condition matched | Withdrawn |
++----------------+-------------------------+------------------------+
+| Not Present | Condition not matched | Advertise |
++----------------+-------------------------+------------------------+
+Here in this topology, based on the default route presence in R2 and
+the configured condition-map (exist-map/non-exist-map) 10.139.224.0/20
+will be either advertised/withdrawn to/from R3.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+ r3 = tgen.add_router("r3")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+ switch.add_link(r2)
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(r2)
+ switch.add_link(r3)
+
+
+def setup_module(mod):
+ 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")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def all_routes_advertised(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": [{"protocol": "bgp"}],
+ "192.0.2.1/32": [{"protocol": "bgp"}],
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": [{"protocol": "bgp"}],
+ "203.0.113.1/32": [{"protocol": "bgp"}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def all_routes_withdrawn(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": None,
+ "192.0.2.5/32": None,
+ "10.139.224.0/20": None,
+ "203.0.113.1/32": None,
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def default_route_withdrawn(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": [{"protocol": "bgp"}],
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": [{"protocol": "bgp"}],
+ "203.0.113.1/32": [{"protocol": "bgp"}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+# BGP conditional advertisement with route-maps
+# EXIST-MAP, ADV-MAP-1 and RMAP-1
+def exist_map_routes_present(router):
+ return all_routes_advertised(router)
+
+
+def exist_map_routes_not_present(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": None,
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": None,
+ "203.0.113.1/32": [{"protocol": "bgp"}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def non_exist_map_routes_present(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": [{"protocol": "bgp"}],
+ "192.0.2.1/32": None,
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": None,
+ "203.0.113.1/32": [{"protocol": "bgp"}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def non_exist_map_routes_not_present(router):
+ return default_route_withdrawn(router)
+
+
+def exist_map_no_condition_route_map(router):
+ return non_exist_map_routes_present(router)
+
+
+def non_exist_map_no_condition_route_map(router):
+ return all_routes_advertised(router)
+
+
+def exist_map_routes_present_rmap_filter(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": [{"protocol": "bgp"}],
+ "192.0.2.5/32": None,
+ "10.139.224.0/20": [{"protocol": "bgp"}],
+ "203.0.113.1/32": None,
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def exist_map_routes_present_no_rmap_filter(router):
+ return all_routes_advertised(router)
+
+
+def non_exist_map_routes_present_rmap_filter(router):
+ return all_routes_withdrawn(router)
+
+
+def non_exist_map_routes_present_no_rmap_filter(router):
+ return non_exist_map_routes_present(router)
+
+
+def exist_map_routes_not_present_rmap_filter(router):
+ return all_routes_withdrawn(router)
+
+
+def exist_map_routes_not_present_no_rmap_filter(router):
+ return exist_map_routes_not_present(router)
+
+
+def non_exist_map_routes_not_present_rmap_filter(router):
+ return exist_map_routes_present_rmap_filter(router)
+
+
+def non_exist_map_routes_not_present_no_rmap_filter(router):
+ return non_exist_map_routes_not_present(router)
+
+
+# BGP conditional advertisement with route-maps
+# EXIST-MAP, ADV-MAP-2 and RMAP-2
+def exist_map_routes_not_present_rmap2_filter(router):
+ return all_routes_withdrawn(router)
+
+
+def exist_map_routes_not_present_no_rmap2_filter(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": [{"protocol": "bgp"}],
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": [{"protocol": "bgp"}],
+ "203.0.113.1/32": None,
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def non_exist_map_routes_not_present_rmap2_filter(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": None,
+ "192.0.2.1/32": None,
+ "192.0.2.5/32": None,
+ "10.139.224.0/20": None,
+ "203.0.113.1/32": [{"protocol": "bgp", "metric": 911}],
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def non_exist_map_routes_not_present_no_rmap2_filter(router):
+ return non_exist_map_routes_not_present(router)
+
+
+def exist_map_routes_present_rmap2_filter(router):
+ return non_exist_map_routes_not_present_rmap2_filter(router)
+
+
+def exist_map_routes_present_no_rmap2_filter(router):
+ return all_routes_advertised(router)
+
+
+def non_exist_map_routes_present_rmap2_filter(router):
+ return all_routes_withdrawn(router)
+
+
+def non_exist_map_routes_present_no_rmap2_filter(router):
+ output = json.loads(router.vtysh_cmd("show ip route json"))
+ expected = {
+ "0.0.0.0/0": [{"protocol": "bgp"}],
+ "192.0.2.1/32": [{"protocol": "bgp"}],
+ "192.0.2.5/32": [{"protocol": "bgp"}],
+ "10.139.224.0/20": [{"protocol": "bgp"}],
+ "203.0.113.1/32": None,
+ }
+ return topotest.json_cmp(output, expected)
+
+
+def exist_map_routes_present_rmap2_network(router):
+ return non_exist_map_routes_not_present_rmap2_filter(router)
+
+
+def exist_map_routes_present_rmap2_no_network(router):
+ return all_routes_withdrawn(router)
+
+
+def non_exist_map_routes_not_present_rmap2_network(router):
+ return non_exist_map_routes_not_present_rmap2_filter(router)
+
+
+def non_exist_map_routes_not_present_rmap2_no_network(router):
+ return all_routes_withdrawn(router)
+
+
+passed = "PASSED!!!"
+failed = "FAILED!!!"
+
+
+def test_bgp_conditional_advertisement_tc_1_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC11: R3 BGP convergence, without advertise-map configuration.
+ # All routes are advertised to R3.
+ test_func = functools.partial(all_routes_advertised, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+
+ msg = 'TC11: "router3" BGP convergence - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_2_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC21: exist-map routes present in R2's BGP table.
+ # advertise-map routes present in R2's BGP table are advertised to R3.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC21: exist-map routes present in "router2" BGP table - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_2_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC22: exist-map routes not present in R2's BGP table
+ # advertise-map routes present in R2's BGP table are withdrawn from R3.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ no network 0.0.0.0/0 route-map DEF
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_not_present, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC22: exist-map routes not present in "router2" BGP table - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_2_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC23: advertise-map with exist-map configuration is removed from a peer
+ # send normal BGP update to advertise previously withdrawn routes if any.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(default_route_withdrawn, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC23: advertise-map with exist-map configuration is removed from peer - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_3_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC31: non-exist-map routes not present in R2's BGP table
+ # advertise-map routes present in R2's BGP table are advertised to R3.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_not_present, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC31: non-exist-map routes not present in "router2" BGP table - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_3_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC32: non-exist-map routes present in R2's BGP table
+ # advertise-map routes present in R2's BGP table are withdrawn from R3.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ network 0.0.0.0/0 route-map DEF
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_present, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC32: non-exist-map routes present in "router2" BGP table - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_3_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC33: advertise-map with non-exist-map configuration is removed from a peer
+ # send normal BGP update to advertisepreviously withdrawn routes if any.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(all_routes_advertised, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = (
+ "TC33: advertise-map with non-exist-map configuration is removed from a peer - "
+ )
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_4_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC41: non-exist-map route-map configuration removed in R2.
+ # advertise-map routes present in R2's BGP table are advertised to R3.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP
+ no route-map EXIST-MAP permit 10
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_no_condition_route_map, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC41: non-exist-map route-map removed in "router2" - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_4_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC42: exist-map route-map configuration removed in R2
+ # advertise-map routes present in R2's BGP table are withdrawn from R3.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_no_condition_route_map, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = 'TC42: exist-map route-map removed in "router2" - '
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_5_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC51: exist-map routes present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ route-map EXIST-MAP permit 10
+ match community DEFAULT-ROUTE
+ match ip address prefix-list DEFAULT-ROUTE
+ !
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-1 out
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC51: exist-map routes present with route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_5_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC52: exist-map routes present in R2's BGP table, no route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-1 out
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_no_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC52: exist-map routes present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_5_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC53: non-exist-map routes present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-1 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_present_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC53: non-exist-map routes present, with route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_5_4():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC54: non-exist-map routes present in R2's BGP table, no route-map filter.
+ # All routes are advertised to R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-1 out
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_present_no_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC54: non-exist-map routes present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_6_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC61: exist-map routes not present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 including advertise-map routes.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ no network 0.0.0.0/0 route-map DEF
+ """
+ )
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-1 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_not_present_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC61: exist-map routes not present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_6_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC62: exist-map routes not present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-1 out
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_not_present_no_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC62: exist-map routes not present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_6_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC63: non-exist-map routes not present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-1 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-1 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_not_present_rmap_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC63: non-exist-map routes not present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_6_4():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC64: non-exist-map routes not present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-1 out
+ """
+ )
+
+ test_func = functools.partial(
+ non_exist_map_routes_not_present_no_rmap_filter, router3
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC64: non-exist-map routes not present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_7_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC71: exist-map routes present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 except advertise-map routes.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ network 0.0.0.0/0 route-map DEF
+ """
+ )
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-2 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC71: exist-map routes present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_7_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC72: exist-map routes present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-2 out
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_no_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC72: exist-map routes present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_7_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC73: non-exist-map routes present in R2's BGP table, with route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-2 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_present_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC73: non-exist-map routes present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_7_4():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC74: non-exist-map routes present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-2 out
+ """
+ )
+
+ test_func = functools.partial(non_exist_map_routes_present_no_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC74: non-exist-map routes present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_8_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC81: exist-map routes not present in R2's BGP table, with route-map filter.
+ # All routes are withdrawn from R3 including advertise-map routes.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ no network 0.0.0.0/0 route-map DEF
+ """
+ )
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-2 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_not_present_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC81: exist-map routes not present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_8_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC82: exist-map routes not present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-2 out
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_not_present_no_rmap2_filter, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC82: exist-map routes not present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_8_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC83: non-exist-map routes not present in R2's BGP table, with route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-2 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(
+ non_exist_map_routes_not_present_rmap2_filter, router3
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC83: non-exist-map routes not present, route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_8_4():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC84: non-exist-map routes not present in R2's BGP table, without route-map filter.
+ # All routes are advertised to R3 including advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no neighbor 10.10.20.3 route-map RMAP-2 out
+ """
+ )
+
+ test_func = functools.partial(
+ non_exist_map_routes_not_present_no_rmap2_filter, router3
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC84: non-exist-map routes not present, no route-map filter - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_9_1():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC91: exist-map routes present in R2's BGP table, with route-map filter and network.
+ # All routes are advertised to R3 including advertise-map routes.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ network 0.0.0.0/0 route-map DEF
+ """
+ )
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ neighbor 10.10.20.3 route-map RMAP-2 out
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_rmap2_network, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC91: exist-map routes present, route-map filter and network - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_9_2():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC92: exist-map routes present in R2's BGP table, with route-map filter and no network.
+ # All routes are advertised to R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no network 203.0.113.1/32
+ """
+ )
+
+ test_func = functools.partial(exist_map_routes_present_rmap2_no_network, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC92: exist-map routes present, route-map filter and no network - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_9_3():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC93: non-exist-map routes not present in R2's BGP table, with route-map filter and network.
+ # All routes are advertised to R3 including advertise-map routes.
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ address-family ipv4 unicast
+ no network 0.0.0.0/0 route-map DEF
+ """
+ )
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ network 203.0.113.1/32
+ neighbor 10.10.20.3 advertise-map ADV-MAP-2 non-exist-map EXIST-MAP
+ """
+ )
+
+ test_func = functools.partial(
+ non_exist_map_routes_not_present_rmap2_network, router3
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC93: non-exist-map routes not present, route-map filter and network - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+def test_bgp_conditional_advertisement_tc_9_4():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # TC94: non-exist-map routes not present in R2's BGP table, with route-map filter and no network.
+ # All routes are advertised to R3 except advertise-map routes.
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 2
+ address-family ipv4 unicast
+ no network 203.0.113.1/32
+ """
+ )
+
+ test_func = functools.partial(
+ non_exist_map_routes_not_present_rmap2_no_network, router3
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=90, wait=1)
+
+ msg = "TC94: non-exist-map routes not present, route-map filter and no network - "
+ assert result is None, msg + failed
+
+ logger.info(msg + passed)
+
+
+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_conditional_advertisement_track_peer/__init__.py b/tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/__init__.py
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf
new file mode 100644
index 0000000..1e98f4e
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+ip prefix-list p1 seq 5 permit 172.16.255.31/32
+!
+route-map r2 permit 10
+ match ip address prefix-list p1
+ set as-path replace 65003
+route-map r2 permit 20
+ set as-path replace any
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf
new file mode 100644
index 0000000..acf120b
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf
new file mode 100644
index 0000000..66c3dc5
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/bgpd.conf
@@ -0,0 +1,28 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ bgp conditional-advertisement timer 5
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 192.168.1.1 advertise-map advertise exist-map exist
+ neighbor 192.168.1.1 route-map deny-all out
+ neighbor 192.168.2.1 route-map exist in
+ exit-address-family
+!
+ip prefix-list exist seq 5 permit 172.16.255.3/32
+ip prefix-list advertise seq 5 permit 172.16.255.2/32
+!
+route-map advertise permit 10
+ match ip address prefix-list advertise
+exit
+!
+route-map exist permit 10
+ match ip address prefix-list exist
+exit
+!
+route-map deny-all deny 10
+exit
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf
new file mode 100644
index 0000000..6c013e2
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/staticd.conf
@@ -0,0 +1,3 @@
+!
+ip route 172.16.255.2/32 r2-eth1
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf
new file mode 100644
index 0000000..f229954
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf
new file mode 100644
index 0000000..5058d40
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 3 10
+ neighbor 192.168.2.2 shutdown
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf
new file mode 100644
index 0000000..268163e
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+int lo
+ ip address 172.16.255.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py b/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py
new file mode 100644
index 0000000..591bac1
--- /dev/null
+++ b/tests/topotests/bgp_conditional_advertisement_track_peer/test_bgp_conditional_advertisement_track_peer.py
@@ -0,0 +1,154 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+Conditionally advertise 172.16.255.2/32 to r1, only if 172.16.255.3/32
+is received from r3.
+
+Also, withdraw if 172.16.255.3/32 disappears.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import (
+ step,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_conditional_advertisement_track_peer():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge():
+ output = json.loads(
+ r2.vtysh_cmd(
+ "show bgp ipv4 unicast neighbors 192.168.1.1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {"172.16.255.2/32": None},
+ "totalPrefixCounter": 0,
+ "filteredPrefixCounter": 0,
+ }
+ return topotest.json_cmp(output, expected)
+
+ # Verify if R2 does not send any routes to R1
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "R2 SHOULD not send any routes to R1"
+
+ step("Enable session between R2 and R3")
+ r3.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ no neighbor 192.168.2.2 shutdown
+ """
+ )
+
+ def _bgp_check_conditional_static_routes_from_r2():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.2/32": [{"valid": True, "nexthops": [{"hostname": "r2"}]}]
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ # Verify if R1 received 172.16.255.2/32 from R2
+ test_func = functools.partial(_bgp_check_conditional_static_routes_from_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "R1 SHOULD receive 172.16.255.2/32 from R2"
+
+ step("Disable session between R2 and R3 again")
+ r3.vtysh_cmd(
+ """
+ configure terminal
+ router bgp
+ neighbor 192.168.2.2 shutdown
+ """
+ )
+
+ # Verify if R2 is not sending any routes to R1 again
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "R2 SHOULD not send any routes to R1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_confederation_astype/__init__.py b/tests/topotests/bgp_confederation_astype/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/__init__.py
diff --git a/tests/topotests/bgp_confederation_astype/r1/bgpd.conf b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf
new file mode 100644
index 0000000..1859a1b
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp confederation identifier 65300
+ bgp confederation peers 65002 65003
+ neighbor fabric peer-group
+ neighbor fabric remote-as external
+ neighbor 192.168.1.2 peer-group fabric
+ neighbor 192.168.2.2 remote-as external
+ address-family ipv4 unicast
+ neighbor fabric activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_confederation_astype/r1/zebra.conf b/tests/topotests/bgp_confederation_astype/r1/zebra.conf
new file mode 100644
index 0000000..608a241
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
+int r1-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_confederation_astype/r2/bgpd.conf b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf
new file mode 100644
index 0000000..697af97
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r2/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ bgp confederation identifier 65300
+ bgp confederation peers 65001
+ neighbor fabric peer-group
+ neighbor fabric remote-as external
+ neighbor 192.168.1.1 peer-group fabric
+ address-family ipv4 unicast
+ network 172.16.255.254/32
+ neighbor fabric activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_confederation_astype/r2/zebra.conf b/tests/topotests/bgp_confederation_astype/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_confederation_astype/r3/bgpd.conf b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf
new file mode 100644
index 0000000..c1f93f6
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r3/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ bgp confederation identifier 65300
+ bgp confederation peers 65001
+ neighbor 192.168.2.1 remote-as external
+ address-family ipv4 unicast
+ network 172.16.255.254/32
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_confederation_astype/r3/zebra.conf b/tests/topotests/bgp_confederation_astype/r3/zebra.conf
new file mode 100644
index 0000000..e5a37c9
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/r3/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r3-eth0
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py
new file mode 100644
index 0000000..5310d3b
--- /dev/null
+++ b/tests/topotests/bgp_confederation_astype/test_bgp_confederation_astype.py
@@ -0,0 +1,140 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if BGP confederation works properly when using
+remote-as internal/external.
+
+Also, check if the same works with peer-groups as well.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2"), "s2": ("r1", "r3")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_confederation_astype():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ def _bgp_converge():
+ output = json.loads(r1.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peerCount": 2,
+ "peers": {
+ "192.168.1.2": {
+ "hostname": "r2",
+ "remoteAs": 65002,
+ "localAs": 65001,
+ "pfxRcd": 1,
+ "state": "Established",
+ },
+ "192.168.2.2": {
+ "hostname": "r3",
+ "remoteAs": 65003,
+ "localAs": 65001,
+ "pfxRcd": 1,
+ "state": "Established",
+ },
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't converge"
+
+ def _bgp_check_neighbors():
+ output = json.loads(r1.vtysh_cmd("show bgp neighbors json"))
+ expected = {
+ "192.168.1.2": {
+ "nbrCommonAdmin": True,
+ "nbrConfedExternalLink": True,
+ "hostname": "r2",
+ },
+ "192.168.2.2": {
+ "nbrCommonAdmin": True,
+ "nbrConfedExternalLink": True,
+ "hostname": "r3",
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_neighbors)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't see neighbors to be in BGP confederation"
+
+ def _bgp_check_routes():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.254/32": [
+ {
+ "valid": True,
+ "pathFrom": "external",
+ "path": "(65003)",
+ },
+ {
+ "valid": True,
+ "pathFrom": "external",
+ "path": "(65002)",
+ },
+ ]
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't see routes to be in BGP confederation"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_afi_safi/__init__.py b/tests/topotests/bgp_default_afi_safi/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/__init__.py
diff --git a/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf
new file mode 100644
index 0000000..bf39152
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r1/bgpd.conf
@@ -0,0 +1,3 @@
+!
+router bgp 65001
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r1/zebra.conf b/tests/topotests/bgp_default_afi_safi/r1/zebra.conf
new file mode 100644
index 0000000..6977651
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
+
diff --git a/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf
new file mode 100644
index 0000000..abbd1b8
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r2/bgpd.conf
@@ -0,0 +1,5 @@
+!
+router bgp 65001
+ no bgp default ipv4-unicast
+ bgp default ipv6-unicast
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r2/zebra.conf b/tests/topotests/bgp_default_afi_safi/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf
new file mode 100644
index 0000000..f3ec3f0
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r3/bgpd.conf
@@ -0,0 +1,5 @@
+!
+router bgp 65001
+ no bgp default ipv4-unicast
+ bgp default l2vpn-evpn
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r3/zebra.conf b/tests/topotests/bgp_default_afi_safi/r3/zebra.conf
new file mode 100644
index 0000000..e9fdfb7
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.255.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf b/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf
new file mode 100644
index 0000000..8a6af55
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r4/bgpd.conf
@@ -0,0 +1,5 @@
+!
+router bgp 65001
+ bgp default ipv6-unicast
+ bgp default l2vpn-evpn
+!
diff --git a/tests/topotests/bgp_default_afi_safi/r4/zebra.conf b/tests/topotests/bgp_default_afi_safi/r4/zebra.conf
new file mode 100644
index 0000000..e9fdfb7
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.255.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
new file mode 100644
index 0000000..eae2a7d
--- /dev/null
+++ b/tests/topotests/bgp_default_afi_safi/test_bgp-default-afi-safi.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if `bgp default ipv4-unicast`, `bgp default ipv6-unicast`
+and `bgp default l2vpn-evpn` commands work as expected.
+
+STEP 1: 'Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only'
+STEP 2: 'Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only'
+STEP 3: 'Check if neighbor 192.168.255.254 is enabled for l2vpn evpn address-family only'
+STEP 4: 'Check if neighbor 192.168.255.254 is enabled for ipv4/ipv6 unicast and l2vpn evpn address-families'
+"""
+
+import os
+import sys
+import json
+import pytest
+
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_ipv4_ipv6_unicast():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Check if neighbor 192.168.255.254 is enabled for ipv4 address-family only")
+
+ def _bgp_neighbor_ipv4_af_only():
+ tgen.gears["r1"].vtysh_cmd(
+ "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external"
+ )
+
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp summary json"))
+
+ if len(output.keys()) == 1 and "ipv4Unicast" in output:
+ return True
+ return False
+
+ assert _bgp_neighbor_ipv4_af_only() == True
+
+ step("Check if neighbor 192.168.255.254 is enabled for ipv6 address-family only")
+
+ def _bgp_neighbor_ipv6_af_only():
+ tgen.gears["r2"].vtysh_cmd(
+ "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external"
+ )
+
+ output = json.loads(tgen.gears["r2"].vtysh_cmd("show bgp summary json"))
+
+ if len(output.keys()) == 1 and "ipv6Unicast" in output:
+ return True
+ return False
+
+ assert _bgp_neighbor_ipv6_af_only() == True
+
+ step("Check if neighbor 192.168.255.254 is enabled for evpn address-family only")
+
+ def _bgp_neighbor_evpn_af_only():
+ tgen.gears["r3"].vtysh_cmd(
+ "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external"
+ )
+
+ output = json.loads(tgen.gears["r3"].vtysh_cmd("show bgp summary json"))
+
+ if len(output.keys()) == 1 and "l2VpnEvpn" in output:
+ return True
+ return False
+
+ assert _bgp_neighbor_evpn_af_only() == True
+
+ step(
+ "Check if neighbor 192.168.255.254 is enabled for ipv4/ipv6 unicast and evpn address-families"
+ )
+
+ def _bgp_neighbor_ipv4_ipv6_and_evpn_af():
+ tgen.gears["r4"].vtysh_cmd(
+ "conf t\nrouter bgp\nneighbor 192.168.255.254 remote-as external"
+ )
+
+ output = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp summary json"))
+
+ if (
+ len(output.keys()) == 3
+ and "ipv4Unicast" in output
+ and "ipv6Unicast" in output
+ and "l2VpnEvpn" in output
+ ):
+ return True
+ return False
+
+ assert _bgp_neighbor_ipv4_ipv6_and_evpn_af() == True
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json b/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json
new file mode 100644
index 0000000..1a3d66b
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/bgp_default_orginate_vrf.json
@@ -0,0 +1,325 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 3024,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r0": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r0": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "1000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "2000",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "3000",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json b/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json
new file mode 100644
index 0000000..9e98235
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/bgp_default_originate_2links.json
@@ -0,0 +1,136 @@
+{
+ "address_types": ["ipv4", "ipv6"],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 3024,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:db8:f::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"r0": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r1": {"dest_link": {"r0": {}}}}}
+ }
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r0": {"dest_link": {"r1": {}}},
+ "r2": {"dest_link": {"r1-link1": {}, "r1-link2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r0": {"dest_link": {"r1": {}}},
+ "r2": {"dest_link": {"r1-link1": {}, "r1-link2": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2-link1": {}, "r2-link2": {}}},
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2-link1": {}, "r2-link2": {}}},
+ "r3": {"dest_link": {"r2": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {}}},
+ "r4": {"dest_link": {"r3": {}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {}}},
+ "r4": {"dest_link": {"r3": {}}}
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}}
+ },
+ "ipv6": {
+ "unicast": {"neighbor": {"r3": {"dest_link": {"r4": {}}}}}
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json b/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json
new file mode 100644
index 0000000..5fae34d
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/bgp_default_originate_topo1.json
@@ -0,0 +1,294 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 3024,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"keepalivetimer": 1,
+ "holddowntimer": 3}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py
new file mode 100644
index 0000000..4d7f436
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_2links.py
@@ -0,0 +1,1810 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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.
+1. Verify default-originate route with default static and network command
+2. Verify default-originate route with aggregate summary command
+3. Verfiy default-originate behaviour in ecmp
+"""
+import os
+import sys
+import time
+import pytest
+import datetime
+from copy import deepcopy
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+)
+from lib.common_config import (
+ verify_fib_routes,
+ step,
+ create_prefix_lists,
+ run_frr_cmd,
+ create_route_maps,
+ shutdown_bringup_interface,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ apply_raw_config,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+)
+
+# 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
+
+# Global variables
+topo = None
+NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"}
+NETWORK1_2 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"}
+NETWORK1_3 = {"ipv4": "198.51.1.3/32", "ipv6": "2001:DB8::1:3/128"}
+NETWORK1_4 = {"ipv4": "198.51.1.4/32", "ipv6": "2001:DB8::1:4/128"}
+NETWORK1_5 = {"ipv4": "198.51.1.5/32", "ipv6": "2001:DB8::1:5/128"}
+
+ipv4_uptime_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": "0.0.0.0/0"},
+ ]
+ }
+}
+
+ipv6_uptime_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": "::/0"},
+ ]
+ }
+}
+
+DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/bgp_default_originate_2links.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_LINK1, DEFAULT_ROUTE_NXT_HOP_LINK2
+ 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
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2-link1"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_LINK1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r1"]["links"]["r2-link2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_LINK2 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+ 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)
+
+
+#####################################################
+#
+# Local API's
+#
+#####################################################
+
+
+def get_rib_route_uptime(tgen, addr_type, dut, input_dict):
+ """
+ Verify route uptime in RIB using "show ip route"
+
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test, for which user wants to test the data
+ * `input_dict` : input dict, has details of static routes
+ * `route_uptime`: uptime of the routes
+
+ Usage
+ -----
+ # Creating static routes for r1
+ input_dict_r1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "147.10.13.4/32"
+ },
+ {
+ "network": "147.10.12.0/24"
+ },
+ {
+ "network": "147.10.13.4/32"
+ },
+ {
+ "network": "147.10.13.4/32"
+ },
+ {
+ "network": "147.10.13.4/32"
+ }
+ ]
+ }
+ }
+
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.info("Entering lib API: get_rib_route_uptime()")
+ route_time = []
+ out_route_dict = {}
+ router_list = tgen.routers()
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s RIB:", router)
+
+ # Verifying RIB routes
+ if addr_type == "ipv4":
+ command = "show ip route"
+ else:
+ command = "show ipv6 route"
+
+ if "static_routes" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ if "vrf" in static_route and static_route["vrf"] is not None:
+
+ logger.info(
+ "[DUT: {}]: Verifying routes for VRF:"
+ " {}".format(router, static_route["vrf"])
+ )
+ cmd = "{} vrf {}".format(command, static_route["vrf"])
+
+ else:
+ cmd = "{}".format(command)
+
+ cmd = "{} json".format(cmd)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if bool(rib_routes_json) is False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+ network = static_route["network"]
+ route_time.append(rib_routes_json[network][0]["uptime"])
+
+ logger.info("Exiting lib API: get_rib_route_uptime()")
+ return route_time
+
+
+def verify_the_uptime(time_stamp_before, time_stamp_after, incremented=None):
+ """
+ time_stamp_before : string the time stamp captured
+ time_stamp_after : string the time stamp captured
+ """
+ uptime_before = datetime.datetime.strptime(time_stamp_before[0], "%H:%M:%S")
+ uptime_after = datetime.datetime.strptime(time_stamp_after[0], "%H:%M:%S")
+
+ if incremented == True:
+ if uptime_before < uptime_after:
+ logger.info(
+ " The Uptime [{}] is incremented than [{}].......PASSED ".format(
+ time_stamp_before, time_stamp_after
+ )
+ )
+ return True
+ else:
+ logger.error(
+ " The Uptime [{}] is expected to be incremented than [{}].......FAILED ".format(
+ time_stamp_before, time_stamp_after
+ )
+ )
+ return False
+ else:
+ logger.info(
+ " The Uptime [{}] is not incremented than [{}] ".format(
+ time_stamp_before, time_stamp_after
+ )
+ )
+ return True
+
+
+def get_best_path_route_in_FIB(tgen, topo, dut, network):
+ """
+ API to verify the best route in FIB and return the ipv4 and ipv6 nexthop for the given route
+ command
+ =======
+ show ip route
+ show ipv6 route
+ params
+ ======
+ dut : device under test :
+ network ; route (ip) to which the best route to be retrieved
+ Returns
+ ========
+ on success : return dict with next hops for the best hop
+ on failure : return error message with boolean False
+ """
+ is_ipv4_best_path_found = False
+ is_ipv6_best_path_found = False
+ rnode = tgen.routers()[dut]
+ ipv4_show_bgp_json = run_frr_cmd(rnode, "sh ip bgp json ", isjson=True)
+ ipv6_show_bgp_json = run_frr_cmd(
+ rnode, "sh ip bgp ipv6 unicast json ", isjson=True
+ )
+ output_dict = {"ipv4": None, "ipv6": None}
+ ipv4_nxt_hop_count = len(ipv4_show_bgp_json["routes"][network["ipv4"]])
+ for index in range(ipv4_nxt_hop_count):
+ if "bestpath" in ipv4_show_bgp_json["routes"][network["ipv4"]][index].keys():
+ best_path_ip = ipv4_show_bgp_json["routes"][network["ipv4"]][index][
+ "nexthops"
+ ][0]["ip"]
+ output_dict["ipv4"] = best_path_ip
+ logger.info(
+ "[DUT [{}]] Best path for the route {} is {} ".format(
+ dut, network["ipv4"], best_path_ip
+ )
+ )
+ is_ipv4_best_path_found = True
+ else:
+ logger.error("ERROR....! No Best Path Found in BGP RIB.... FAILED")
+
+ ipv6_nxt_hop_count = len(ipv6_show_bgp_json["routes"][network["ipv6"]])
+ for index in range(ipv6_nxt_hop_count):
+ if "bestpath" in ipv6_show_bgp_json["routes"][network["ipv6"]][index].keys():
+ ip_add_count = len(
+ ipv6_show_bgp_json["routes"][network["ipv6"]][index]["nexthops"]
+ )
+ for i_index in range(ip_add_count):
+ if (
+ "global"
+ in ipv6_show_bgp_json["routes"][network["ipv6"]][index]["nexthops"][
+ i_index
+ ]["scope"]
+ ):
+ best_path_ip = ipv6_show_bgp_json["routes"][network["ipv6"]][index][
+ "nexthops"
+ ][i_index]["ip"]
+ output_dict["ipv6"] = best_path_ip
+ logger.info(
+ "[DUT [{}]] Best path for the route {} is {} ".format(
+ dut, network["ipv6"], best_path_ip
+ )
+ )
+
+ else:
+ logger.error("ERROR....! No Best Path Found in BGP RIB.... FAILED")
+ if is_ipv4_best_path_found:
+ return output_dict
+ else:
+ logger.error("ERROR...! Unable to find the Best Path in the RIB")
+ return False
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_verify_bgp_default_originate_with_default_static_route_p1(request):
+ """
+ Summary: "Verify default-originate route with default static and network command "
+
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE, DEFAULT_ROUTE_NXT_HOP_LINK1, DEFAULT_ROUTE_NXT_HOP_LINK2, DEFAULT_ROUTES
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step("Configure 2 link between R1 and R2")
+ step("Configure IPV4 and IPV6 EBGP between R1 and R2 both the links")
+ step("Configure default-originate on R1 IPv4 and IPv6 BGP session link-1 only ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {"r2": {"dest-link": "r1-link1"}}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {"r2": {"dest-link": "r1-link1"}}
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4/IPv6 default originate routes present on R2 nexthop as link-1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_LINK1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure network command on R1 (0.0.0.0/0 and 0::0/0) for IPv4 and IPv6 address family "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {"network": [DEFAULT_ROUTES[addr_type]]}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("No change on IPv4/IPv6 default-originate route advertised from link1")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Before removing default originate from R1 link -1 IPv4 and IPv6 address family taking the uptime snapshot"
+ )
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Remove default originate from R1 link -1 IPv4 and IPv6 address family ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Routes must be learned from network command")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("After removing the default originate on R1 taking the uptime snapshot")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After removing the default-originate uptime should get reset for link-1 learn route"
+ )
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before configuring default - originate")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "Configure default-originate on R1 link-1 again for IPv4 and IPv6 address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify No change on R2 routing and BGP table for both the links ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking snapshot after configuring default - originate")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After configuring the default-originate uptime should not get reset for link-1 learn route"
+ )
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=True)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=True)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before removing network 0.0.0.0 ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Remove network command from R1 IPv4/IPv6 address family ")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify 0.0.0.0/0 and 0::0/0 route get removed from link-2 and default-originate IPv4/IPv6 route learn on link-1"
+ )
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route from link2 is not expected \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n Route from link2 is not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After removing default originate command on R1 verify that the uptime got reset on R2"
+ )
+
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before configuring static route network")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "Configure static default route for IPv4 and IPv6 (0.0.0.0/0 next-hop Null0 and 0::0/0 next-hop Null0) on R1"
+ )
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "0.0.0.0/0",
+ "next_hop": NEXT_HOP_IP["ipv4"],
+ },
+ {
+ "network": "0::0/0",
+ "next_hop": NEXT_HOP_IP["ipv6"],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verifyIPv4 and IPv6 static routes are configure and up on R1 ")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "0.0.0.0/0",
+ "next_hop": NEXT_HOP_IP["ipv4"],
+ },
+ {
+ "network": "0::0/0",
+ "next_hop": NEXT_HOP_IP["ipv6"],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static on IPv4 and IPv6 address family")
+ redistribute_static = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify No change on IPv4/IPv6 default-originate route advertised from link1")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n Best Path sould be advertised in routes\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Taking uptime snapshot before removing default originate")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Remove default-originate from link-1 from IPv4 and IPv6 neighbor ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot after removing default originate")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("verify the up time , up time should get reset ")
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify No change on IPv4/IPv6 default-originate route advertised from link1")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before configuring default originate")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ " Configure default-originate on link-1 again for IPv4 and IPv6 address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify No change on IPv4/IPv6 default-originate route advertised from link1")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot after configuring default originate")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("After configuring the default originate the uptime should not get reset ")
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before removing redisctribute static ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Remove redistribute static from IPv4 and IPv6 address family ")
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "local_as": get_dut_as_number(tgen, dut="r1"),
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify No change on IPv4/IPv6 default-originate route advertised from link1")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taking uptime snapshot before removing redisctribute static ")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("After removing default originate the route uptime should get reset ")
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=True)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=True)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_default_originate_with_aggregate_summary_p1(request):
+ """
+ Summary: "Verify default-originate route with aggregate summary command"
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure default-originate on R1 IPv4 and IPv6 BGP session link-1 only")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {"r2": {"dest-link": "r1-link1"}}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {"r2": {"dest-link": "r1-link1"}}
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4/IPv6 default originate routes present on R2 nexthop as link-1,on R2"
+ )
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure 5 static route for IPv4 and IPv6 on R0")
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK1_3[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK1_4[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK1_5[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Before configuring the aggregate route taking uptime snapshot ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Configure aggregate summary command for IPv4 and IPv6 address family ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(local_as),
+ "address-family ipv4 unicast",
+ "aggregate-address {} summary-only".format("0.0.0.0/0 "),
+ "exit-address-family",
+ "address-family ipv6 unicast",
+ "aggregate-address {} summary-only".format("0::0/0"),
+ "exit-address-family",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "verify that no change on IPv4/IPv6 default-originate route advertised from link1 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 on R2"
+ )
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("After configuring the aggregate route taking uptime snapshot ")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After Configuring the aggregate route uptime should get reset for link-1 learn route"
+ )
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Before removing default originate taking uptime snapshot ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("Remove default originate from R1 link -1 IPv4 and IPv6 address family")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {"dest-link": "r1-link1", "delete": True}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that no change on IPv4/IPv6 default-originate route advertised from link1 0.0.0.0/0 and 0::0/0 route also get advertised from link-2 on R2"
+ )
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("After removing default origin taking uptime snapshot ")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After removing the default-originate uptime should get reset for link-1 learn route"
+ )
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Before Configuring default origin taking uptime snapshot ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "Configure default-originate on R1 link-1 again for IPv4 and IPv6 address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "dest-link": "r1-link1",
+ }
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("After Configuring default originate taking uptime snapshot")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step(
+ "After Configuring the default-originate uptime should get reset for link-1 learn route"
+ )
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Before removing aggregate -summary command taking the uptime snapshot ")
+ uptime_before_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_before_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("remove aggregate summary command for IPv4 and IPv6 address family ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(local_as),
+ "address-family ipv4 unicast",
+ "no aggregate-address {} summary-only".format("0.0.0.0/0"),
+ "exit-address-family",
+ "address-family ipv6 unicast",
+ "no aggregate-address {} summary-only".format("0::0/0"),
+ "exit-address-family",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify Default-originate IPv4/IPv6 route learn on link-1 ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK1,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify 0.0.0.0/0 and 0::0/0 route get removed from link-2 ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_LINK2,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("After removing aggregate -summary command taking the uptime snapshot ")
+ uptime_after_ipv4 = get_rib_route_uptime(tgen, "ipv4", "r2", ipv4_uptime_dict)
+ uptime_after_ipv6 = get_rib_route_uptime(tgen, "ipv6", "r2", ipv6_uptime_dict)
+
+ step("After removing aggregate command uptime should get reset ")
+ result = verify_the_uptime(uptime_before_ipv4, uptime_after_ipv4, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_the_uptime(uptime_before_ipv6, uptime_after_ipv6, incremented=False)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ write_test_footer(tc_name)
+
+
+def test_verify_default_originate_with_2way_ecmp_p2(request):
+ """
+ Summary: "Verify default-originate route with 3 way ECMP and traffic "
+ """
+
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step("Populating next-hops details")
+ r1_r2_ipv4_neighbor_ips = []
+ r1_r2_ipv6_neighbor_ips = []
+ r1_link = None
+ for index in range(1, 3):
+ r1_link = "r1-link" + str(index)
+ r1_r2_ipv4_neighbor_ips.append(
+ topo["routers"]["r2"]["links"][r1_link]["ipv4"].split("/")[0]
+ )
+ r1_r2_ipv6_neighbor_ips.append(
+ topo["routers"]["r2"]["links"][r1_link]["ipv6"].split("/")[0]
+ )
+
+ step(
+ "Configure default-originate on R1 for all the neighbor of IPv4 and IPv6 peers "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ for index in range(2):
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(local_as),
+ "address-family ipv4 unicast",
+ "neighbor {} default-originate".format(
+ r1_r2_ipv4_neighbor_ips[index]
+ ),
+ "exit-address-family",
+ "address-family ipv6 unicast",
+ "neighbor {} default-originate ".format(
+ r1_r2_ipv6_neighbor_ips[index]
+ ),
+ "exit-address-family",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+
+ r2_link = None
+ for index in range(1, 3):
+ r2_link = "r2-link" + str(index)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0]
+ interface = topo["routers"]["r1"]["links"][r2_link]["interface"]
+ ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ping R1 configure IPv4 and IPv6 loopback address from R2")
+ pingaddr = topo["routers"]["r1"]["links"]["lo"]["ipv4"].split("/")[0]
+ router = tgen.gears["r2"]
+ output = router.run("ping -c 4 -w 4 {}".format(pingaddr))
+ assert " 0% packet loss" in output, "Ping R1->R2 FAILED"
+ logger.info("Ping from R1 to R2 ... success")
+
+ step("Shuting up the active route")
+ network = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"}
+ ipv_dict = get_best_path_route_in_FIB(tgen, topo, dut="r2", network=network)
+ dut_links = topo["routers"]["r1"]["links"]
+ active_interface = None
+ for key, values in dut_links.items():
+ ipv4_address = dut_links[key]["ipv4"].split("/")[0]
+ ipv6_address = dut_links[key]["ipv6"].split("/")[0]
+ if ipv_dict["ipv4"] == ipv4_address and ipv_dict["ipv6"] == ipv6_address:
+ active_interface = dut_links[key]["interface"]
+
+ logger.info(
+ "Shutting down the interface {} on router {} ".format(active_interface, "r1")
+ )
+ shutdown_bringup_interface(tgen, "r1", active_interface, False)
+
+ step("Verify the complete convergence to fail after shutting the interface")
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ result is not True
+ ), " Testcase {} : After shuting down the interface Convergence is expected to be Failed".format(
+ tc_name
+ )
+
+ step(
+ "Verify routes from active best path is not received from r1 after shuting the interface"
+ )
+ r2_link = None
+ for index in range(1, 3):
+ r2_link = "r2-link" + str(index)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0]
+ interface = topo["routers"]["r1"]["links"][r2_link]["interface"]
+ ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop}
+ if index == 1:
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ping R1 configure IPv4 and IPv6 loopback address from R2")
+ pingaddr = topo["routers"]["r1"]["links"]["lo"]["ipv4"].split("/")[0]
+ router = tgen.gears["r2"]
+ output = router.run("ping -c 4 -w 4 {}".format(pingaddr))
+ assert " 0% packet loss" in output, "Ping R1->R2 FAILED"
+ logger.info("Ping from R1 to R2 ... success")
+
+ step("No Shuting up the active route")
+
+ shutdown_bringup_interface(tgen, "r1", active_interface, True)
+
+ step("Verify the complete convergence after bringup the interface")
+ result = verify_bgp_convergence(tgen, topo)
+ assert (
+ result is True
+ ), " Testcase {} : After bringing up the interface complete convergence is expected ".format(
+ tc_name
+ )
+
+ step("Verify all the routes are received from r1 after no shuting the interface")
+ r2_link = None
+ for index in range(1, 3):
+ r2_link = "r2-link" + str(index)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0]
+ interface = topo["routers"]["r1"]["links"][r2_link]["interface"]
+ ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop}
+ if index == 1:
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure IPv4 and IPv6 route-map with deny option on R2 to filter default route 0.0.0.0/0 and 0::0/0"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ input_dict_3 = {
+ "r2": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": DEFAULT_ROUTES["ipv4"],
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": DEFAULT_ROUTES["ipv6"],
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Apply route-map IN direction of R2 ( R2-R1) for IPv4 and IPv6 BGP neighbors")
+ r2_link = None
+ for index in range(1, 3):
+ r2_link = "r2-link" + str(index)
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ r2_link: {
+ "route_maps": [
+ {"name": "RMv4", "direction": "in"}
+ ]
+ },
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ r2_link: {
+ "route_maps": [
+ {"name": "RMv6", "direction": "in"}
+ ]
+ },
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("After applying the route-map the routes are not expected in RIB ")
+ r2_link = None
+ for index in range(1, 3):
+ r2_link = "r2-link" + str(index)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"][r2_link]["ipv4"].split("/")[0]
+ interface = topo["routers"]["r1"]["links"][r2_link]["interface"]
+ ipv6_link_local_nxt_hop = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local_nxt_hop}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert result is not 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/bgp_default_originate/test_bgp_default_originate_topo1_1.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py
new file mode 100644
index 0000000..8142723
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_1.py
@@ -0,0 +1,2539 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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.
+1. Verify BGP default-originate route with IBGP peer
+2. Verify BGP default-originate route with EBGP peer
+3. Verify BGP default route when default-originate configured with route-map over IBGP peer
+4. Verify BGP default route when default-originate configured with route-map over EBGP peer"
+
+"""
+import os
+import sys
+import time
+import pytest
+from time import sleep
+from copy import deepcopy
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_router_id,
+ modify_as_number,
+ verify_as_numbers,
+ clear_bgp_and_verify,
+ clear_bgp,
+ verify_bgp_rib,
+ get_prefix_count_route,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+ verify_bgp_advertised_routes_from_neighbor,
+ verify_bgp_received_routes_from_neighbor,
+)
+from lib.common_config import (
+ interface_status,
+ verify_prefix_lists,
+ verify_fib_routes,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+ step,
+ required_linux_kernel_version,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_prefix_lists,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+ delete_route_maps,
+)
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# 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
+
+# Global variables
+topo = None
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+# Global variables
+NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"}
+NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"}
+NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"}
+NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"}
+NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"}
+NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"}
+NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"}
+NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"}
+NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"}
+NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"}
+DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+IPV4_RM = "RMVIPV4"
+IPV6_RM = "RMVIPV6"
+
+IPV4_RM1 = "RMVIPV41"
+IPV6_RM1 = "RMVIPV61"
+
+IPV4_RM2 = "RMVIPV42"
+IPV6_RM2 = "RMVIPV62"
+
+IPV4_PL_1 = "PV41"
+IPV4_PL_2 = "PV42"
+
+IPV6_PL_1 = "PV61"
+IPV6_PL_2 = "PV62"
+
+
+r1_ipv4_loopback = "1.0.1.0/24"
+r2_ipv4_loopback = "1.0.2.0/24"
+r3_ipv4_loopback = "1.0.3.0/24"
+r4_ipv4_loopback = "1.0.4.0/24"
+r1_ipv6_loopback = "2001:db8:f::1:0/120"
+r2_ipv6_loopback = "2001:db8:f::2:0/120"
+r3_ipv6_loopback = "2001:db8:f::3:0/120"
+r4_ipv6_loopback = "2001:db8:f::4:0/120"
+
+r0_connected_address_ipv4 = "192.168.0.0/24"
+r0_connected_address_ipv6 = "fd00::/64"
+r1_connected_address_ipv4 = "192.168.1.0/24"
+r1_connected_address_ipv6 = "fd00:0:0:1::/64"
+r3_connected_address_ipv4 = "192.168.2.0/24"
+r3_connected_address_ipv6 = "fd00:0:0:2::/64"
+r4_connected_address_ipv4 = "192.168.3.0/24"
+r4_connected_address_ipv6 = "fd00:0:0:3::/64"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_default_originate_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3
+ global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP
+ global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP
+ global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP
+ global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP
+
+ 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
+ )
+ # There are the global varibles used through out the file these are acheived only after building the topology.
+
+ r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"]
+ r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv4"
+ ].split("/")[0]
+ r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"]
+ r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv6"
+ ].split("/")[0]
+
+ r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"]
+ r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[0]
+ r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"]
+ r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv6"
+ ].split("/")[0]
+
+ r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"]
+ r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"]
+ r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ R0_NETWORK_LOOPBACK = {
+ "ipv4": r0_loopback_address_ipv4,
+ "ipv6": r0_loopback_address_ipv6,
+ }
+ R0_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_LOOPBACK = {
+ "ipv4": r1_loopback_address_ipv4,
+ "ipv6": r1_loopback_address_ipv6,
+ }
+ R1_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R0_NETWORK_CONNECTED = {
+ "ipv4": r0_connected_address_ipv4,
+ "ipv6": r0_connected_address_ipv6,
+ }
+ R0_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_CONNECTED = {
+ "ipv4": r1_connected_address_ipv4,
+ "ipv6": r1_connected_address_ipv6,
+ }
+ R1_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R4_NETWORK_LOOPBACK = {
+ "ipv4": r4_loopback_address_ipv4,
+ "ipv6": r4_loopback_address_ipv6,
+ }
+ R4_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_LOOPBACK = {
+ "ipv4": r3_loopback_address_ipv4,
+ "ipv6": r3_loopback_address_ipv6,
+ }
+ R3_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ R4_NETWORK_CONNECTED = {
+ "ipv4": r4_connected_address_ipv4,
+ "ipv6": r4_connected_address_ipv6,
+ }
+ R4_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_CONNECTED = {
+ "ipv4": r3_connected_address_ipv4,
+ "ipv6": r3_connected_address_ipv6,
+ }
+ R3_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ # populating the nexthop for default routes
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ 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 test_verify_bgp_default_originate_in_IBGP_p0(request):
+ """
+ Verify BGP default-originate route with IBGP peer
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2")
+ step("Configure IPv4 and IPv6 Loopback interface on R1, R0 and R2")
+ step("Configure IPv4 and IPv6 EBGP neighbor between R0 and R1")
+
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 2000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 2000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": r3_local_as,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": r4_local_as,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, " Complete Convergence is expected after changing the ASN but failed to converge --> :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure IPv4 and IPv6 static route on R1 next-hop as NULL0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed to configure the static routes {} on router R1 \n Error: {}".format(
+ tc_name,static_routes_input, result
+ )
+ step("verify IPv4 and IPv6 static route are configured and up on R1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n After configuring the static routes {} , the routes are not found in FIB \n Error: {}".format(
+ tc_name,static_routes_input, result
+ )
+
+ step(
+ "Configure redistribute static and connected on R0 and R1, for IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed to configure the redistribute static configuration \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring redistribute command , verify static and connected routes ( loopback connected routes) are advertised on R2"
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : After redistributing static routes the routes {} expected in FIB but NOT FOUND ......! \n Error: {}".format(
+ tc_name, static_routes_input,result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : After redistributing static routes the routes {} expected in RIB but NOT FOUND ......! \n Error: {}".format(
+ tc_name, static_routes_input , result
+ )
+
+ step(
+ "Taking the snapshot of the prefix count before configuring the default originate"
+ )
+ snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r1")
+
+ step(
+ "Configure Default originate on R1 for R1 to R2, for IPv4 and IPv6 BGP address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed Configuring default originate configuration. \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ " R1 static and loopback routes received on R2 BGP and FIB"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : post configuring the BGP Default originate configuration static and connected routes should not be effected but impacted on FIB .......! FAILED \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failedpost configuring the BGP Default originate configuration static and connected routes should not be effected but impacted on RIB......! FAILED \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Verify default route for IPv4 and IPv6 present with path=igp metric =0 , local-preference= 100 "
+ )
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ locPrf=100,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ step(
+ "Taking the snapshot2 of the prefix count after configuring the default originate"
+ )
+ snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r1")
+
+ step("verifying the prefix count incrementing or not ")
+ isIPv4prefix_incremented = False
+ isIPv6prefix_incremented = False
+ if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]:
+ isIPv4prefix_incremented = True
+ if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]:
+ isIPv6prefix_incremented = True
+
+ assert (
+ isIPv4prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+ assert (
+ isIPv6prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_default_originate_in_EBGP_p0(request):
+ """
+ Verify BGP default-originate route with EBGP peer
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2")
+ step("Configure lPv4 and IPv6 Loopback interface on R3, R4 and R2")
+ step("Configure IPv4 and IPv6 IBGP neighbor between R4 and R3")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": r1_local_as,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": r2_local_as,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "Complete convergence is expeceted after changing the ASN os the routes ..! :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(" Configure IPv4 and IPv6 static route on R3 next-hop on R4 interface")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed to configure the static routes ....! Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify IPv4 and IPv6 static route are configured and up on R1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Route is not found in {} in FIB ......! Failed \n Error: {}".format(
+ tc_name, static_routes_input,result
+ )
+
+ step(
+ "Configure redistribute static and connected on R3 and R4 for IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed to configure redistribute configuratin \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring redistribute command , verify static and connected routes ( loopback connected routes) are advertised on R2"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : static & and connected routes are expected but not found in FIB .... ! \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : static & and connected routes are expected but not found in RIB .... ! \n Error: {}".format(
+ tc_name, result
+ )
+ snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3")
+ step(
+ "Configure Default originate on R3 for R3 to R2, on IPv4 and IPv6 BGP address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed to configure the default originate configuration \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 on both BGP RIB and FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : static route from R1 {} and default route from R3 is expected in R2 FIB .....! NOT FOUND \n Error: {}".format(
+ tc_name, NETWORK1_1,result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : static route from R1 {} and default route from R3 is expected in R2 RIB .....! NOT FOUND \n Error: {}".format(
+ tc_name,NETWORK1_1, result
+ )
+
+ step(
+ "Verify default route for IPv4 and IPv6 present with path = ebgp as path, metric =0 "
+ )
+ # local preference will bgp not applicable for eBGP
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=0,
+ expected_aspath="4000",
+ )
+ assert result is True, "Testcase {} : Default route from R3 is expected with attributes in R2 RIB .....! NOT FOUND Error: {}".format(tc_name, result)
+
+ step(
+ "Taking the snapshot2 of the prefix count after configuring the default originate"
+ )
+ snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3")
+ step(
+ "Verify out-prefix count is incremented default route on IPv4 and IPv6 neighbor"
+ )
+ isIPv4prefix_incremented = False
+ isIPv6prefix_incremented = False
+ if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]:
+ isIPv4prefix_incremented = True
+ if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]:
+ isIPv6prefix_incremented = True
+
+ assert (
+ isIPv4prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+ assert (
+ isIPv6prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_default_originate_in_IBGP_with_route_map_p0(request):
+ """
+ test_verify_bgp_default_originate_in_IBGP_with_route_map_p0
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2")
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R0")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": r3_local_as,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": r4_local_as,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "Complete convergence is expected after changing ASN ....! ERROR :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure 2 IPv4 and 2 IPv6 Static route on R0 with next-hop as Null0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Static Configuration is Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert result is True, "Testcase {} : routes {} unable is not found in R0 FIB \n Error: {}".format(
+ tc_name, static_routes_input,result
+ )
+
+ step(
+ "Configure redistribute static on IPv4 and IPv6 address family on R0 for R0 to R1 neighbor "
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed to configure redistribute static configuration....! \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 static route are configured and up on R1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed... Routes {} expected in r0 FIB after configuring the redistribute config \n Error: {}".format(
+ tc_name,static_routes_input, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed... Routes {} expected in r0 RIB after configuring the redistribute config \n Error: {}".format(
+ tc_name, static_routes_input,result
+ )
+
+ step(
+ "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R1 to match BGP route Sv41, Sv42, IPv6 route Sv61 Sv62 permit "
+ )
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the prefix list \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPV4 and IPv6 route-map (RMv4 and RMv6 ) matching prefix-list (Pv4 and Pv6) respectively on R1"
+ )
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the route map \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure default-originate with route-map (RMv4 and RMv6) on R1, on BGP IPv4 and IPv6 address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed to configure the default originate \n Error: {}".format(tc_name, result)
+
+ step("Verify the default route is received in BGP RIB and FIB")
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed...! Expected default route from R1 not found in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed...! Expected default route from R1 not found in RIB \n Error: {}".format(
+ tc_name, result
+ )
+ step("Remove route-map RMv4 and RMv6 from default-originate command in R1")
+ NOTE = """ Configuring the default-originate should remove the previously applied default originate with condtional route-map"""
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed to remove the default originate conditional route-map \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP RIB and FIB After removing route-map , default route still present on R2"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default route from R1 is not found in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default route from R1 is not found in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default-originate with route-map (RMv4 and RMv6) on R1 ")
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "route_map": "RMv4",
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "default_originate": {
+ "r2": {
+ "route_map": "RMv6",
+ }
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed to configure the Default originate route-map \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default Route from R1 is not found in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default Route from R1 is not found in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete prefix list using no prefix-list")
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ "delete": True,
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ "delete": True,
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to delete the prefix list Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP RIB and FIB After deleting prefix-list , verify IPv4 and IPv6 default route got removed from DUT "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n After deleteing prefix default route is not expected in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After deleteing prefix default route is not expected in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure prefix-list and delete route-map using no route-map")
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the prefix lists Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring the Prefixlist cross checking the BGP Default route is configured again , before deleting the route map"
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed Default route from R1 is expected in FIB but not found \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed Default route from R1 is expected in RIB but not found \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Deleting the routemap")
+ input_dict = {"r1": {"route_maps": ["RMv4", "RMv6"]}}
+ result = delete_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed to delete the Route-map \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP RIB and FIB ,After deleting route-map , verify IPv4 and IPv6 default route got removed from DUT"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After deleteing route-map default route is not expected in FIB \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After deleteing route-map default route is not expected in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_bgp_default_originate_in_EBGP_with_route_map_p0(request):
+ """
+ test_verify_bgp_default_originate_in_EBGP_with_route_map_p0
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2")
+ step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": r1_local_as,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": r2_local_as,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Configure 2 IPv4 and 2 IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed to configure the static routes \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify IPv4 and IPv6 static route are configured and up on R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed Static route {} is not found in R4 FIB \n Error: {}".format(
+ tc_name, static_routes_input,result
+ )
+
+ step(
+ "Configure redistribute static on IPv4 and IPv6 address family on R4 for R4 to R3 neighbo"
+ )
+ redistribute_static = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed to configure the redistribute static \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 static route are configured and up on R3")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed static routes from R1 and R3 is not found in FIB \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed static routes from R1 and R3 is not found in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R3 so new route which is not present on R3"
+ )
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK3_1["ipv4"],
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK3_1["ipv6"],
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the prefix lists \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed ..! configured prefix lists {} are not found \n Error: {}".format(tc_name,input_dict, result)
+
+ step(
+ "Configure IPv4 and IPv6 route-map ( RMv4 and RMv6 ) matching prefix-list (Pv4 and Pv6 ) respectively on R3"
+ )
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the route-map \n Error: {}".format(tc_name, result)
+ step(
+ "Taking the snapshot of the prefix count before configuring the default originate"
+ )
+ snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3")
+ step(
+ "Configure default-originate with IPv4 and IPv6 route-map (RMv4 and RMv6) on R3"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed to configure default-originate \n Error: {}".format(tc_name, result)
+
+ step("Verify the default route is NOT received in BGP RIB and FIB on R2 ")
+ step(
+ "After configuring default-originate command , verify default routes are not Received on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Default route is not expected due to deny in prefix list \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nDefault route is not expected due to deny in prefix list\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to configure the prefix lists Error: {}".format(tc_name, result)
+
+ step("Verify BGP default route for IPv4 and IPv6 is received on R2")
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default routes are expected in R2 FIB from R3 but not found ....! \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed Default routes are expected in R2 RIB from R3 but not found ....! \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ "delete": True,
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ "delete": True,
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed to remove prefix-lists from R3 Error: {}".format(tc_name, result)
+
+ step(
+ "After Removing route BGP default route for IPv4 and IPv6 is NOT received on R2"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After Removing route in prefix list the default route is not expected in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After Removing route in prefix list the default route is not expected in RIB\n Error: {}".format(
+ tc_name, result
+ )
+
+ step(" Add route Sv41, Sv42, IPv6 route Sv61 Sv62 on prefix list Pv4 and Pv6")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify BGP default route for IPv4 and IPv6 is received on R2")
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change IPv4 and IPv6 prefix-list permit and deny ")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {"seqid": "1", "network": NETWORK1_1["ipv4"], "action": "deny"},
+ {"seqid": "2", "network": NETWORK2_1["ipv4"], "action": "deny"},
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {"seqid": "1", "network": NETWORK1_1["ipv6"], "action": "deny"},
+ {"seqid": "2", "network": NETWORK2_1["ipv6"], "action": "deny"},
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify BGP default route for IPv4 and IPv6 is not received on R2")
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n after denying the prefix list default route is not expected in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n after denying the prefix list default route is not expected in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change IPv4 and IPv6 prefix-list deny to permit ")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify BGP default route for IPv4 and IPv6 is received on R2")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Taking the snapshot2 of the prefix count after configuring the default originate"
+ )
+ snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3")
+
+ step("verifying the prefix count incrementing or not ")
+ isIPv4prefix_incremented = False
+ isIPv6prefix_incremented = False
+ if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]:
+ isIPv4prefix_incremented = True
+ if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]:
+ isIPv6prefix_incremented = True
+
+ assert (
+ isIPv4prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+ assert (
+ isIPv6prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV6 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+ step(
+ "Configure another IPv4 and IPv6 route-map and match same prefix-list (Sv41, Sv42, IPv6 route Sv61 Sv62) with deny statement "
+ )
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMv41": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv61": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Attach route-map on IPv4 and IP6 BGP neighbor on fly")
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv41"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv61"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "After attaching route-map verify IPv4 and IPv6 default route is withdrawn from the R2"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change the recently added Routemap from deny to permit")
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMv41": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv61": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 default route is advertised from the R2")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Delete default-originate route-map command while configuring ( neighbor x.x.x default-originate) for IPv4 and IPv6 BGP neighbor "
+ )
+ """ Configuring the Default originate on neighbor must remove the previously assigned deault-originate with routemap config """
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify in running config from BGP that default-originate with route-map command is removed and default-originate command is still present and default route for IPv4 and IPv6 present in RIB and FIB"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate with conditional route-map command on IPv4 and IPv6 address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv41"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv61"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify in running config from BGP that default-originate with route-map command is present and default route for IPv4 and IPv6 present"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Delete default originate with 'no bgp default-originate' from IPV4 and IPV6 address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ " Verify in running config from BGP that default-originate complete CLI is removed for IPV4 and IPV6 address family and default originate routes got deleted"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Default Route is not expected in FIB \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Default Route is not expected in RIB\nError: {}".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/bgp_default_originate/test_bgp_default_originate_topo1_2.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py
new file mode 100644
index 0000000..8e6f930
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_2.py
@@ -0,0 +1,2439 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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.
+5. Verify BGP default originate route-map with OUT route-map
+6. Verify BGP default originate route-map with IN route-map
+8. Verify BGP default route after removing default-originate
+9. Verify default-originate route with GR
+"""
+import os
+import sys
+import time
+import pytest
+from time import sleep
+from copy import deepcopy
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_router_id,
+ modify_as_number,
+ verify_as_numbers,
+ clear_bgp_and_verify,
+ clear_bgp,
+ verify_bgp_rib,
+ get_prefix_count_route,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+ verify_bgp_advertised_routes_from_neighbor,
+ verify_bgp_received_routes_from_neighbor,
+)
+from lib.common_config import (
+ interface_status,
+ verify_prefix_lists,
+ verify_fib_routes,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+ step,
+ required_linux_kernel_version,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_prefix_lists,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+ delete_route_maps,
+)
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# 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
+
+# Global variables
+topo = None
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+# Global variables
+NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"}
+NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"}
+NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"}
+NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"}
+NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"}
+NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"}
+NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"}
+NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"}
+NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"}
+NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"}
+DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+IPV4_RM = "RMVIPV4"
+IPV6_RM = "RMVIPV6"
+
+IPV4_RM1 = "RMVIPV41"
+IPV6_RM1 = "RMVIPV61"
+
+IPV4_RM2 = "RMVIPV42"
+IPV6_RM2 = "RMVIPV62"
+
+IPV4_PL_1 = "PV41"
+IPV4_PL_2 = "PV42"
+
+IPV6_PL_1 = "PV61"
+IPV6_PL_2 = "PV62"
+
+
+r1_ipv4_loopback = "1.0.1.0/24"
+r2_ipv4_loopback = "1.0.2.0/24"
+r3_ipv4_loopback = "1.0.3.0/24"
+r4_ipv4_loopback = "1.0.4.0/24"
+r1_ipv6_loopback = "2001:db8:f::1:0/120"
+r2_ipv6_loopback = "2001:db8:f::2:0/120"
+r3_ipv6_loopback = "2001:db8:f::3:0/120"
+r4_ipv6_loopback = "2001:db8:f::4:0/120"
+
+r0_connected_address_ipv4 = "192.168.0.0/24"
+r0_connected_address_ipv6 = "fd00::/64"
+r1_connected_address_ipv4 = "192.168.1.0/24"
+r1_connected_address_ipv6 = "fd00:0:0:1::/64"
+r3_connected_address_ipv4 = "192.168.2.0/24"
+r3_connected_address_ipv6 = "fd00:0:0:2::/64"
+r4_connected_address_ipv4 = "192.168.3.0/24"
+r4_connected_address_ipv6 = "fd00:0:0:3::/64"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_default_originate_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3
+ global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP
+ global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP
+ global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP
+ global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP
+
+ 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
+ )
+ # There are the global varibles used through out the file these are acheived only after building the topology.
+
+ r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"]
+ r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv4"
+ ].split("/")[0]
+ r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"]
+ r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv6"
+ ].split("/")[0]
+
+ r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"]
+ r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[0]
+ r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"]
+ r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv6"
+ ].split("/")[0]
+
+ r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"]
+ r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"]
+ r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ R0_NETWORK_LOOPBACK = {
+ "ipv4": r0_loopback_address_ipv4,
+ "ipv6": r0_loopback_address_ipv6,
+ }
+ R0_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_LOOPBACK = {
+ "ipv4": r1_loopback_address_ipv4,
+ "ipv6": r1_loopback_address_ipv6,
+ }
+ R1_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R0_NETWORK_CONNECTED = {
+ "ipv4": r0_connected_address_ipv4,
+ "ipv6": r0_connected_address_ipv6,
+ }
+ R0_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_CONNECTED = {
+ "ipv4": r1_connected_address_ipv4,
+ "ipv6": r1_connected_address_ipv6,
+ }
+ R1_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R4_NETWORK_LOOPBACK = {
+ "ipv4": r4_loopback_address_ipv4,
+ "ipv6": r4_loopback_address_ipv6,
+ }
+ R4_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_LOOPBACK = {
+ "ipv4": r3_loopback_address_ipv4,
+ "ipv6": r3_loopback_address_ipv6,
+ }
+ R3_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ R4_NETWORK_CONNECTED = {
+ "ipv4": r4_connected_address_ipv4,
+ "ipv6": r4_connected_address_ipv6,
+ }
+ R4_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_CONNECTED = {
+ "ipv4": r3_connected_address_ipv4,
+ "ipv6": r3_connected_address_ipv6,
+ }
+ R3_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ # populating the nexthop for default routes
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ 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)
+
+
+#####################################################
+#
+# Local API's
+#
+#####################################################
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_verify_bgp_default_originate_route_map_in_OUT_p1(request):
+ """
+ test_verify_bgp_default_originate_route_map_in_OUT_p1
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2")
+ step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": r1_local_as,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": r2_local_as,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Configure 2 IPv4 and 2 IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static knob on R4 , for R4 to R3 neighbor ")
+ redistribute_static = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ expected_routes = {
+ "ipv4": [
+ {"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK2_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ ],
+ "ipv6": [
+ {"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK2_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r4", peer="r3", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "After redistribute static verify the routes is recevied in router R3 in RIB and FIB"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R3 to match BGP route Sv41, IPv6 route Sv61 with permit option "
+ )
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPv4 and IPv6 route-map RMv4 and RMv6 matching prefix-list Pv4 and Pv6 with permit option "
+ )
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RM4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RM6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPv4 prefix-list Pv42 and and IPv6 prefix-list Pv62 on R3 to match BGP route Sv42, IPv6 route Sv62 with deny option"
+ )
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv42": [
+ {"seqid": "1", "network": NETWORK2_1["ipv4"], "action": "deny"}
+ ]
+ },
+ "ipv6": {
+ "Pv62": [
+ {"seqid": "1", "network": NETWORK2_1["ipv6"], "action": "deny"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r3": {"prefix_lists": ["Pv42", "Pv62"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPv4 and IPv6 route-map (RMv42 and RMv62 )matching prefix-list Pv42 and Pv62 with permit option "
+ )
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMv42": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv42"}},
+ },
+ ],
+ "RMv62": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv62"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Apply IPv4 and IPv6 route-map RMv4 and RMv6 with default-originate on R3 , for R3 to R2 peers and Apply IPv4 and IPv6 out route-map RMv42 and RMv62 on R3 , for R3 to R2 peers "
+ )
+ local_as = get_dut_as_number(tgen, "r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RM4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RM6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ updated_topo = topo
+ updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0")
+ updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1")
+ updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2")
+ updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3")
+ updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4")
+
+ step(
+ "Apply IPv4 and IPv6 route-map RMv42 and RMv62 on R3 (OUT Direction), for R3 to R2 peers "
+ )
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {"name": "RMv42", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {"name": "RMv62", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, updated_topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ NOTE = """
+ After applying route-map on neighbor verify default BGP route IPv4 IPv6 route populated in R2 BGP and routing table , verify using "show ip bgp json" "show ipv6 bgp json" "show ip route json" "show ip route json"
+ Sv42 and Sv62 route should not be present on R2
+ """
+ step(NOTE)
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Static routes are not expected due to conditions \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Static routes are not expected due to conditions\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change IPv4 prefix-list Pv42 and and IPv6 prefix-list Pv62 deny to permit")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv42": [
+ {
+ "seqid": "1",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "Pv62": [
+ {
+ "seqid": "1",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r3": {"prefix_lists": ["Pv42", "Pv62"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ NOTE = """Default BGP route and IPv4 ( Sv42) , IPv6 (Sv62) route populated in R2 BGP and routing table"""
+ step(NOTE)
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 permit to deny ")
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {"seqid": "1", "network": NETWORK1_1["ipv4"], "action": "deny"}
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {"seqid": "1", "network": NETWORK1_1["ipv6"], "action": "deny"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ NOTE = """
+ Verify default-originate route (IPv4 and IPv6 ) not present on R2
+ IPv4 ( Sv42) , IPv6 (Sv62) route populated in R2 BGP
+ """
+ step(NOTE)
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n default-route in RIB is not expected due to conditions \n Error: {}".format(
+ tc_name, result
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+def test_verify_bgp_default_originate_route_map_in_IN_p1(request):
+ """Verify BGP default originate route-map with IN route-map"""
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R2")
+ step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R0")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": r2_local_as,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": r3_local_as,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": r4_local_as,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Configure 2 IPv4 and 2 IPv6, Static route on R0 with next-hop as Null0 IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verifyIPv4 and IPv6 static routes are configure and up on R0 ")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute static knob on R0 , for R0 to R1 IPv4 and IPv6 neighbor"
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 route received on R1 ")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure IPv4 prefix-list Pv4 and and IPv6 prefix-list Pv6 on R1 to match BGP route Sv41, Sv42, IPv6 route Sv61 Sv62"
+ )
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ {
+ "seqid": "2",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R1")
+ input_dict = {"r1": {"prefix_lists": ["Pv4", "Pv6"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPv4 and IPv6 route-map RMv4 and RMv6 matching prefix-list Pv4 and Pv6 with deny option on R1"
+ )
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Apply route-map IN direction in R1 (R1 to R0) IPv4 and IPv6 neighbor")
+ updated_topo = topo
+ updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0")
+ updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1")
+ updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2")
+ updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3")
+ updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4")
+
+ local_as_r1 = get_dut_as_number(tgen, dut="r1")
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RMv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r0": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RMv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, updated_topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ STEP = "After applying route-map verify that IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62 should not present on R1 BGP and routing table "
+ step(STEP)
+
+ step(
+ "After applying route-map verify that IPv4 route Sv41, Sv42, IPv6 route Sv61 Sv62 should not present on R1 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen, addr_type, "r1", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n default-route in FIB is not expected due to conditions \nError: {}".format(
+ tc_name, result
+ )
+ # Routes should come to dut but not the shown in RIB thus verifying using show ip bgp nbr xxx received route
+ step(
+ " Verify the received routes \n using 'show ip bgp nbr xxx received route' in Router R1"
+ )
+ expected_routes = {
+ "ipv4": [
+ {"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK2_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]},
+ ],
+ "ipv6": [
+ {"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv6"]},
+ {"network": NETWORK2_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv6"]},
+ ],
+ }
+ result = verify_bgp_received_routes_from_neighbor(
+ tgen, topo, dut="r1", peer="r0", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure default-originate on R1 for R1 to R2 IPv4 and IPv6 neighbor ")
+ local_as_r1 = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as_r1,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Default originate knob is configured and default route advertised to R2 , verify on R1 "
+ )
+ expected_routes = {
+ "ipv4": [
+ {"network": "0.0.0.0/0", "nexthop": ""},
+ ],
+ "ipv6": [
+ {"network": "::/0", "nexthop": ""},
+ ],
+ }
+ result = verify_bgp_advertised_routes_from_neighbor(
+ tgen, topo, dut="r1", peer="r2", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the Default route Route in FIB in R2")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change route-map RMv4 and RMv6 from deny to permit")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ NOTE = """After changing route-map to permit verify that IPv4 routes Sv41, Sv42, IPv6 routes Sv61 Sv62 present on R1 BGP and routing table , using "show ip route " "show ip bgp nbr xxx received route " "show ipv6 route " "show ipv6 bgp nbr xxx receied route """
+ step(NOTE)
+ expected_routes = {
+ "ipv4": [{"network": NETWORK1_1["ipv4"], "nexthop": NEXT_HOP_IP["ipv4"]}],
+ "ipv6": [{"network": NETWORK1_1["ipv6"], "nexthop": NEXT_HOP_IP["ipv4"]}],
+ }
+ result = verify_bgp_received_routes_from_neighbor(
+ tgen, topo, dut="r1", peer="r0", expected_routes=expected_routes
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default static route (IPv4 and IPv6) on R2 nexthop as R1 ")
+ NEXT_HOP_IP_R1 = {}
+ r1_r2_ipv4_neighbor = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ r1_r2_ipv6_neighbor = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ NEXT_HOP_IP_R1["ipv4"] = r1_r2_ipv4_neighbor
+ NEXT_HOP_IP_R1["ipv6"] = r1_r2_ipv6_neighbor
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "0.0.0.0/0",
+ "next_hop": NEXT_HOP_IP_R1["ipv4"],
+ },
+ {
+ "network": "0::0/0",
+ "next_hop": NEXT_HOP_IP_R1["ipv6"],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Static default route is taking preference over BGP default routes , BGP default route is inactive IN RIB and static is up and installed in RIB and FIB "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop}
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP[addr_type],
+ "protocol": "static",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+def test_verify_default_originate_after_removing_default_originate_p1(request):
+ """Verify BGP default route after removing default-originate"""
+
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure EBGP between R0 to R1 and IBGP between R1 to R2")
+ step("Configure EBGP between R2 to R3 and IBGP between R3 to R4")
+ r0_local_as = topo["routers"]["r0"]["bgp"]["local_as"]
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r2_local_as = topo["routers"]["r2"]["bgp"]["local_as"]
+ r3_local_as = topo["routers"]["r3"]["bgp"]["local_as"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 2000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 2000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 5000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 5000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure IPv4 and IPv6 static route on R0 and R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ },
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R0 and R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 static route are configured and up on R1 and R3")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R1 and R3")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify all the static , connected and loopback routes from R0,R1,R3 and R4 is receieved on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the Default Originate on R2 nexthop as R1")
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in RIB -> {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in FIB -> {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer "
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ STEP = """After configuring the Default Originate From R3 --> R2
+ Both Default routes from R1 and R3 Should present in R2 BGP RIB
+ The Deafult Route from iBGP is prefferedover EBGP thus
+ Default Route From R1->r2 should only present in R2 FIB """
+ step(STEP)
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Only IBGP default originate is expected in FIB over EBGP {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "No change on static and connected routes which got advertised from R0, R1, R3 and R4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " Remove default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the Default Originate reoute from R1 to r2 is removed in R2 ")
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After removing the default originate the route should not be present in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After removing the default originate the route should not be present in RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ NOTE = """ after removing the Default originate from R1-->R2
+ Verify the BGP Default route received from R3 is present in both BGP RIB and FIB on R2
+ """
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "No change on static and connected routes which got advertised from R0, R1, R3 and R4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer "
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"delete": True}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "After removing default originate , verify default IPv4 and IPv6 BGP routes removed on R2 from R1 ( next-hop as R3) "
+ )
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After removing the default originate the route should not be present in FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After removing the default originate the route should not be present in RIB \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "No change on static and connected routes which got advertised from R0, R1, R3 and R4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+def test_verify_default_originate_route_with_GR_p1(request):
+ """ "Verify default-originate route with GR "
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+
+ step("Configure IPV4 and IPV6 IBGP between R1 and R2 ")
+ step("Configure IPV4 and IPV6 EBGP between R2 to R3 ")
+ r0_local_as = topo['routers']['r0']['bgp']['local_as']
+ r1_local_as = topo['routers']['r1']['bgp']['local_as']
+ r2_local_as = topo['routers']['r2']['bgp']['local_as']
+ r3_local_as = topo['routers']['r3']['bgp']['local_as']
+ r4_local_as = topo['routers']['r4']['bgp']['local_as']
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": r3_local_as,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": r4_local_as,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Configure per peer Graceful restart on R2 ( restarting router) and R3 helper router "
+ )
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "local_as": get_dut_as_number(tgen, "r2"),
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": get_dut_as_number(tgen, "r3"),
+ "graceful-restart": {"graceful-restart-helper": True},
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r2", peer="r3")
+
+ step("verify Graceful restart at R2")
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r3"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Configure default-originate on R1 for R1-R2 neighbor for IPv4 and IPv6 BGP peers "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate":{
+ "r2":{
+
+ }
+
+ }
+
+ }
+ }, "ipv6": {
+ "unicast": {
+ "default_originate":{
+ "r2":{
+
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step(
+ "R2 received default-originate routes and advertised it to R3 , verify on R2 and R3"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+ step(" Kill BGPd session on R2")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ step("verify default route is relearned after clear bgp on R2 on BGP RIB and")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input,next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type])
+ 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/bgp_default_originate/test_bgp_default_originate_topo1_3.py b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py
new file mode 100644
index 0000000..9e52504
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_bgp_default_originate_topo1_3.py
@@ -0,0 +1,2540 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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.
+10. Verify default-originate route after BGP and FRR process restart
+11. Verify default-originate route after shut/no shut and clear BGP neighbor
+"""
+import os
+import sys
+import time
+import pytest
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ modify_as_number,
+ clear_bgp,
+ verify_bgp_rib,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+)
+from lib.common_config import (
+ interface_status,
+ verify_prefix_lists,
+ verify_fib_routes,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+ step,
+ required_linux_kernel_version,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_prefix_lists,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+)
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# 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
+
+# Global variables
+topo = None
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+# Global variables
+NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"}
+NETWORK2_1 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"}
+DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+r0_connected_address_ipv4 = "192.168.0.0/24"
+r0_connected_address_ipv6 = "fd00::/64"
+r1_connected_address_ipv4 = "192.168.1.0/24"
+r1_connected_address_ipv6 = "fd00:0:0:1::/64"
+r3_connected_address_ipv4 = "192.168.2.0/24"
+r3_connected_address_ipv6 = "fd00:0:0:2::/64"
+r4_connected_address_ipv4 = "192.168.3.0/24"
+r4_connected_address_ipv6 = "fd00:0:0:3::/64"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_default_originate_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls micronet initialization functions.
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3
+ global R0_NETWORK_LOOPBACK, R0_NETWORK_LOOPBACK_NXTHOP, R1_NETWORK_LOOPBACK
+ global R0_NETWORK_CONNECTED, R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP
+ global R4_NETWORK_LOOPBACK, R4_NETWORK_LOOPBACK_NXTHOP, R3_NETWORK_LOOPBACK
+ global R4_NETWORK_CONNECTED, R4_NETWORK_CONNECTED_NXTHOP, R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP
+
+ 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
+ )
+ # There are the global varibles used through out the file these are acheived only after building the topology.
+
+ r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"]
+ r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv4"
+ ].split("/")[0]
+ r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"]
+ r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv6"
+ ].split("/")[0]
+
+ r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"]
+ r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[0]
+ r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"]
+ r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv6"
+ ].split("/")[0]
+
+ r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"]
+ r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"]
+ r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ R0_NETWORK_LOOPBACK = {
+ "ipv4": r0_loopback_address_ipv4,
+ "ipv6": r0_loopback_address_ipv6,
+ }
+ R0_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_LOOPBACK = {
+ "ipv4": r1_loopback_address_ipv4,
+ "ipv6": r1_loopback_address_ipv6,
+ }
+
+ R0_NETWORK_CONNECTED = {
+ "ipv4": r0_connected_address_ipv4,
+ "ipv6": r0_connected_address_ipv6,
+ }
+ R0_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r0_loopback_address_ipv4_nxt_hop,
+ "ipv6": r0_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_CONNECTED = {
+ "ipv4": r1_connected_address_ipv4,
+ "ipv6": r1_connected_address_ipv6,
+ }
+ R1_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R4_NETWORK_LOOPBACK = {
+ "ipv4": r4_loopback_address_ipv4,
+ "ipv6": r4_loopback_address_ipv6,
+ }
+ R4_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_LOOPBACK = {
+ "ipv4": r3_loopback_address_ipv4,
+ "ipv6": r3_loopback_address_ipv6,
+ }
+ R4_NETWORK_CONNECTED = {
+ "ipv4": r4_connected_address_ipv4,
+ "ipv6": r4_connected_address_ipv6,
+ }
+ R4_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r4_loopback_address_ipv4_nxt_hop,
+ "ipv6": r4_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_CONNECTED = {
+ "ipv4": r3_connected_address_ipv4,
+ "ipv6": r3_connected_address_ipv6,
+ }
+ R3_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ # populating the nexthop for default routes
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ 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 test_verify_default_originate_after_BGP_and_FRR_restart_p2(request):
+ """
+ Summary: "Verify default-originate route after BGP and FRR process restart "
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure EBGP between R0 to R1 and IBGP between R1 to R2")
+ step("Configure EBGP between R2 to R3 and IBGP between R3 to R4")
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": 999,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert (
+ BGP_CONVERGENCE is True
+ ), " Failed convergence after chaning the AS number :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure IPv4 and IPv6 static route (Sv4 , Sv6) on R0 and (S1v4, S1v6)on R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the static route on R0 \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the static route on R4 \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed Route {} not found in R0 FIB \n Error: {}".format(
+ tc_name, NETWORK1_1, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed Route {} not found in R4 FIB \n Error: {}".format(
+ tc_name, NETWORK2_1, result
+ )
+
+ step(
+ "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family"
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the static route \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Redistributed routes from R0 is not learned in Router R1 RIB \n Error: {}".format(
+ tc_name, result
+ )
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Redistributed routes from R0 is not learned in Router R1 FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R3")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Redistributed routes from R4 is not learned in Router R3 RIB \n Error: {}".format(
+ tc_name, result
+ )
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Redistributed routes from R4 is not learned in Router R3 FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure IPv4 and IPv6 prefix-list on R1 for (Sv4 , Sv6) route")
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the prefix lists \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the Prefix - lists")
+ input_dict = {"r3": {"prefix_lists": ["Pv4", "Pv6"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to verify the prefix lists in router R3 \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure IPv4 (RMv4) and IPv6 (RMv6) route-map on R1")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route-map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " Configure default originate with route-map RMv4 and RMv6 for IPv4 and IPv6 bgp neighbors on R1 ( R1-R2) "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the default-originate in R1 towards R2 \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 default route received on R2 with R1 nexthop ")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Default routes are not learned in R2 FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Default routes are not learned in R2 RIB\n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute connected and static on R1 IPv4 and IPv6 address family"
+ )
+ redistribute_static = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 and IPv6 static and loopback route advertised from R4 and R0 are received on R2"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(" Configure default-originate on R3 for R3 to R2 IPv4 and IPv6 BGP neighbors ")
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ STEP = """After configuring the Default Originate From R3 --> R2
+ Both Default routes from R1 and R3 Should present in R2 BGP RIB.
+ 'The Deafult Route from iBGP is preffered over EBGP' thus
+ Default Route From R1->r2 should only present in R2 FIB """
+ step(STEP)
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n IBGP default route should be preffered over EBGP default-originate \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the default route from R1 is recieved both on RIB and FIB on R2")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=False,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify the static and loopback route advertised from R0 and R4 are received on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(" BGP Daemon restart operation")
+ routers = ["r1", "r2"]
+ for dut in routers:
+ step(
+ "Restart BGPD process on {}, when all the processes are running use watchfrr ".format(
+ dut
+ )
+ )
+ kill_router_daemons(tgen, dut, ["bgpd"])
+ start_router_daemons(tgen, dut, ["bgpd"])
+
+ step("After restarting the BGP daomon Verify the default originate ")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n IBGP default route should be prefeered over EBGP \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify the default route from R1 is is recieved both on RIB and FIB on R2"
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=False,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify the static and loopback route advertised from R0 and R4 are received on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(" Restarting FRR routers operation")
+ """
+ NOTE : Verify that iBGP default route is preffered over eBGP default route
+ """
+ routers = ["r1", "r2"]
+ for dut in routers:
+ step(
+ "Restart FRR router process on {}, when all the processes are running use watchfrr ".format(
+ dut
+ )
+ )
+
+ stop_router(tgen, dut)
+ start_router(tgen, dut)
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert (
+ result is True
+ ), " Testcase {} : After Restarting {} Convergence Failed".format(tc_name, dut)
+
+ step("After restarting the FRR Router Verify the default originate ")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify the default route from R1 is is recieved both on RIB and FIB on R2"
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n IBGP default route should be preffered over EBGP default route \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify the static and loopback route advertised from R0 and R4 are received on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_default_originate_after_shut_no_shut_bgp_neighbor_p1(request):
+ """
+ Summary: "Verify default-originate route after shut/no shut and clear BGP neighbor "
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure EBGP between R0 to R1 and IBGP between R1 to R2")
+ step("Configure EBGP between R2 to R3 and IBGP between R3 to R4")
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": 999,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure one IPv4 and one IPv6 static route on R0 and R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static route configured on R0 and R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute connected and static on R0 (R0-R1) on R4 ( R4-R3) IPv4 and IPv6 address family"
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ },
+ {
+ "redist_type": "connected",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ },
+ {
+ "redist_type": "connected",
+ },
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ },
+ {
+ "redist_type": "connected",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ },
+ {
+ "redist_type": "connected",
+ },
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "connected",
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "connected",
+ }
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 static route configured on R1 from R0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify IPv4 and IPv6 static route configured on R3 from R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify IPv4 and IPv6 bgp default route received on R2 nexthop as R1")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 from R0 and R4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate on R3 for R3 to R2 neighbor for IPv4 and IPv6 peer"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ STEP = """After configuring the Default Originate From R3 --> R2
+ Both Default routes from R1 and R3 Should present in R2 BGP RIB.
+ 'The Deafult Route from iBGP is preffered over EBGP' thus
+ Default Route From R1->r2 should only present in R2 FIB """
+ step(STEP)
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n IBGP default route should be preffered over EBGP \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the default route from R1 is recieved both on RIB and FIB on R2")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring default-originate command , verify static ,connected and loopback routes are advertised on R2 from R0 and R4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ # updating the topology with the updated AS-Number to avoid conflict in con configuring the AS
+ updated_topo = topo
+ updated_topo["routers"]["r0"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r0")
+ updated_topo["routers"]["r1"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r1")
+ updated_topo["routers"]["r2"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r2")
+ updated_topo["routers"]["r3"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r3")
+ updated_topo["routers"]["r4"]["bgp"]["local_as"] = get_dut_as_number(tgen, "r4")
+
+ step(
+ "Shut R1 to R2 IPv4 and IPv6 BGP neighbor from R1 IPv4 and IPv6 address family "
+ )
+
+ local_as = get_dut_as_number(tgen, dut="r1")
+ shut_neighbor = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"shutdown": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"shutdown": True}}}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ input_dict = {"r2": {"interface_list": [interface], "status": "down"}}
+
+ result = interface_status(tgen, topo, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Bring down interface failed ! \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify IPv4 and IPv6 default static and loopback route which received from R1 are deleted from R2"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n after shutting down interface routes are not expected \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n after shutting down interface routes are not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify that No impact on IPv4 IPv6 and default route received from R3 ")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "No-Shut R1 to R2 IPv4 and IPv6 BGP neighbor from R1 IPv4 and IPv6 address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ shut_neighbor = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"shutdown": False}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"shutdown": False}}}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ input_dict = {"r2": {"interface_list": [interface], "status": "up"}}
+
+ result = interface_status(tgen, topo, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result)
+
+ step(
+ "After no shut Verify IPv4 and IPv6 bgp default route next hop as R1 , static ,connected and loopback received on R2 from r0 and r4 "
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Shut R3 to R2 IPv4 and IPv6 BGP neighbor from R2 IPv4 and IPv6 address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ shut_neighbor = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"shutdown": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"shutdown": True}}}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo["routers"]["r2"]["links"]["r3"]["interface"]
+ input_dict = {"r2": {"interface_list": [interface], "status": "down"}}
+
+ result = interface_status(tgen, topo, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Bring down interface failed ! \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify IPv4 and IPv6 default static and loopback route which received from R3 are deleted from R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n After shutting down the interface routes are not expected \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_routes(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After shutting down the interface routes are not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that Default route is removed i.e advertised from R3")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After shutting down the interface Default route are not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n After shutting down the interface Default route are not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that No impact on IPv4 IPv6 and default route received from R1")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "No-Shut R3 to R2 IPv4 and IPv6 BGP neighbor from R2 IPv4 and IPv6 address family"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ shut_neighbor = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"shutdown": False}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3": {"shutdown": False}}}
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo["routers"]["r2"]["links"]["r3"]["interface"]
+ input_dict = {"r2": {"interface_list": [interface], "status": "up"}}
+
+ result = interface_status(tgen, topo, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that a static ,connected and loopback routes are received from R0 and R4 on R2 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify that default route is received on R2 from R1")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that default route is received on R2 from R3")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Clear IPv4 and IP6 BGP session from R2 and R1 one by one ")
+ routers = ["r1", "r2"]
+ for dut in routers:
+ for addr_type in ADDR_TYPES:
+
+ clear_bgp(tgen, addr_type, dut)
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify that default route is received on R2 from R3")
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ DEFAULT_ROUTE_NXT_HOP = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify the static , loopback and connected routes received from r0 and r4"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut BGP neighbor interface R2 (R2 to R1) link ")
+ intf_r2_r1 = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_r1, False)
+
+ step("Verify the bgp Convergence ")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ BGP_CONVERGENCE is not True
+ ), " :Failed After shutting interface BGP convergence is expected to be faileed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify that default route from R1 got deleted from BGP and RIB table")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\n After shuting interface default route should be removed from RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("No - Shut BGP neighbor interface R2 (R2 to R1) link ")
+ intf_r2_r1 = topo["routers"]["r2"]["links"]["r1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_r1, True)
+
+ step("Verify the bgp Convergence ")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("verify that default route is received on R2 from R3")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the static , loopback and connected routes received from r0 and r4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut link from R3 to R2 from R3")
+ intf_r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "r3", intf_r3_r2, False)
+
+ step("Verify the bgp Convergence ")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ BGP_CONVERGENCE is not True
+ ), " :Failed \nAfter Shuting the interface BGP convegence is expected to be failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Verify that default route from R3 got deleted from BGP and RIB table")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("No-Shut link from R3 to R2 from R3")
+
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+
+ DEFAULT_ROUTE_NXT_HOP_1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop}
+
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+
+ DEFAULT_ROUTE_NXT_HOP_3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_nxt_hop}
+
+ intf_r3_r2 = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "r3", intf_r3_r2, True)
+
+ step("Verify the bgp Convergence ")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo, expected=True)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("verify that default route is received on R2 from R3")
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the static , loopback and connected routes received from r0 and r4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R0_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R0_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R0_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R4_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R4_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R4_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_fib_routes(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ 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/bgp_default_originate/test_default_orginate_vrf.py b/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py
new file mode 100644
index 0000000..fa5164f
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_default_orginate_vrf.py
@@ -0,0 +1,1389 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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.
+#
+import os
+import sys
+import time
+import pytest
+from time import sleep
+from copy import deepcopy
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_router_id,
+ modify_as_number,
+ verify_as_numbers,
+ clear_bgp_and_verify,
+ clear_bgp,
+ verify_bgp_rib,
+ get_prefix_count_route,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+ verify_bgp_advertised_routes_from_neighbor,
+ verify_bgp_received_routes_from_neighbor,
+)
+from lib.common_config import (
+ interface_status,
+ verify_prefix_lists,
+ verify_rib,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+ step,
+ required_linux_kernel_version,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_prefix_lists,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+ delete_route_maps,
+)
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# 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
+
+# Global variables
+topo = None
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+
+
+# Global variables
+NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"}
+NETWORK2_1 = {"ipv4": "198.51.1.2/32", "ipv6": "2001:DB8::1:2/128"}
+NETWORK5_1 = {"ipv4": "198.51.1.3/32", "ipv6": "2001:DB8::1:3/128"}
+NETWORK5_2 = {"ipv4": "198.51.1.4/32", "ipv6": "2001:DB8::1:4/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+
+r0_connected_address_ipv4 = "192.168.0.0/24"
+r0_connected_address_ipv6 = "fd00::/64"
+r1_connected_address_ipv4 = "192.168.1.0/24"
+r1_connected_address_ipv6 = "fd00:0:0:1::/64"
+r3_connected_address_ipv4 = "192.168.2.0/24"
+r3_connected_address_ipv6 = "fd00:0:0:2::/64"
+r4_connected_address_ipv4 = "192.168.3.0/24"
+r4_connected_address_ipv6 = "fd00:0:0:3::/64"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_default_orginate_vrf.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3
+ global R1_NETWORK_LOOPBACK, R1_NETWORK_LOOPBACK_NXTHOP
+ global R0_NETWORK_CONNECTED_NXTHOP, R1_NETWORK_CONNECTED, R1_NETWORK_CONNECTED_NXTHOP
+ global R3_NETWORK_LOOPBACK, R3_NETWORK_LOOPBACK_NXTHOP
+ global R3_NETWORK_CONNECTED, R3_NETWORK_CONNECTED_NXTHOP
+
+ 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
+ )
+ # There are the global varibles used through out the file these are acheived only after building the topology.
+
+ r0_loopback_address_ipv4 = topo["routers"]["r0"]["links"]["lo"]["ipv4"]
+ r0_loopback_address_ipv4_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv4"
+ ].split("/")[0]
+ r0_loopback_address_ipv6 = topo["routers"]["r0"]["links"]["lo"]["ipv6"]
+ r0_loopback_address_ipv6_nxt_hop = topo["routers"]["r0"]["links"]["r1"][
+ "ipv6"
+ ].split("/")[0]
+
+ r1_loopback_address_ipv4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_loopback_address_ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r1_loopback_address_ipv6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_loopback_address_ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ r4_loopback_address_ipv4 = topo["routers"]["r4"]["links"]["lo"]["ipv4"]
+ r4_loopback_address_ipv4_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[0]
+ r4_loopback_address_ipv6 = topo["routers"]["r4"]["links"]["lo"]["ipv6"]
+ r4_loopback_address_ipv6_nxt_hop = topo["routers"]["r4"]["links"]["r3"][
+ "ipv6"
+ ].split("/")[0]
+
+ r3_loopback_address_ipv4 = topo["routers"]["r3"]["links"]["lo"]["ipv4"]
+ r3_loopback_address_ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv4"
+ ].split("/")[0]
+ r3_loopback_address_ipv6 = topo["routers"]["r3"]["links"]["lo"]["ipv6"]
+ r3_loopback_address_ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"][
+ "ipv6"
+ ].split("/")[0]
+
+ R1_NETWORK_LOOPBACK = {
+ "ipv4": r1_loopback_address_ipv4,
+ "ipv6": r1_loopback_address_ipv6,
+ }
+ R1_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R1_NETWORK_CONNECTED = {
+ "ipv4": r1_connected_address_ipv4,
+ "ipv6": r1_connected_address_ipv6,
+ }
+ R1_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r1_loopback_address_ipv4_nxt_hop,
+ "ipv6": r1_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_LOOPBACK = {
+ "ipv4": r3_loopback_address_ipv4,
+ "ipv6": r3_loopback_address_ipv6,
+ }
+ R3_NETWORK_LOOPBACK_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ R3_NETWORK_CONNECTED = {
+ "ipv4": r3_connected_address_ipv4,
+ "ipv6": r3_connected_address_ipv6,
+ }
+ R3_NETWORK_CONNECTED_NXTHOP = {
+ "ipv4": r3_loopback_address_ipv4_nxt_hop,
+ "ipv6": r3_loopback_address_ipv6_nxt_hop,
+ }
+
+ # populating the nexthop for default routes
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ 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 test_verify_default_originate_route_with_non_default_VRF_p1(request):
+ """
+ "Verify default-originate route with non-default VRF"
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ # these steps are implemented as base toplopgy setup
+ step("Configure IPV4 and IPV6 IBGP between R1 and R2 default VRF")
+ step("Configure IPV4 and IPV6 EBGP between R2 to R3 non-default VRF (RED)")
+ step(
+ "Configure IPv4 and IP6 loopback address on R1 default and R3 non-default (RED) VRF"
+ )
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ step(
+ "Configure IPv4 and IPv6 static route on R1 default and R3 non-default (RED) VRF with nexthop as Null ( different static route on each side)"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the static routes in router R1 default vrf \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure static route in R3 non default vrf RED \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify IPv4 and IPv6 static route configured on R1 default vrf and R3 non-default (RED) vrf"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "r1", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed: Routes configured on vrf is not seen in R1 default VRF FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, "r3", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed : Routes configured in non-defaul vrf in R3 FIB is \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute connected and static on R1 (R1-R2) and on R3 ( R2-R3 RED VRF) IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 3000,
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ },
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the redistribute on R1 and R3 \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify IPv4 and IPv6 static route configured on R1 received as BGP routes on R2 default VRF "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Verify IPv4 and IPv6 static route configured on R3 received as BGP routes on R2 non-default VRF "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer"
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the default originate \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED")
+
+ step(
+ "Configure default-originate on R3 for R3 to R2 neighbor (RED VRF) for IPv4 and IPv6 peer"
+ )
+
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": "3000",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 and IPv6 bgp default route and static route received on R2 VRF red nexthop as R3"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify Out-prefix count incremented for IPv4/IPv6 default route on VRF red")
+ snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED")
+ step("verifying the prefix count incrementing or not ")
+ isIPv4prefix_incremented = False
+ isIPv6prefix_incremented = False
+ if snapshot1["ipv4_count"] < snapshot2["ipv4_count"]:
+ isIPv4prefix_incremented = True
+ if snapshot1["ipv6_count"] < snapshot2["ipv6_count"]:
+ isIPv6prefix_incremented = True
+
+ assert (
+ isIPv4prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+ step("Configure import VRF red on R2 for IPV4 and IPV6 BGP peer")
+ step("Importing the non-default vrf in default VRF ")
+ local_as = get_dut_as_number(tgen, "r2")
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "RED"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "RED"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify VRF RED IPv4 and IPv6, default-originate, \n static and loopback route are imported to R2 default VRF table ,\n default-originate route coming from VRF red should not active on R2 default VRF table"
+ )
+ step("verifying the static routes connected and loop back routes")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ STEP = """ After importing non defualt VRF into default vrf .
+ verify that the default originate from R1 --> R2(non -default) is preffered over R3 --> R2
+ because the Default Route prefers iBGP over eBGP over
+ Default Route from R1 Should be present in BGP RIB and FIB
+ Default Route from R3 Should be present only in BGP RIB not in FIB
+ """
+ step(STEP)
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Configure import VRF default on R2 (R2-R3) RED VRF for IPV4 and IPV6 BGP peer"
+ )
+ step("Importing the default vrf in non-default VRF ")
+ local_as = "2000"
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "default"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "default"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Default VR, IPv4 and IPv6 , default-originate, \n static and loopback route are imported to R2 VRF RED table \n, default-originate route coming from VRF red should not active on R2 default VRF table"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ STEP = """ After importing defualt VRF into non default vrf .
+ verify that the default originate from R1 --> R2(non -default) is preffered over R3 --> R2
+ because the Default Route prefers iBGP over eBGP over
+ Default Route from R1 Should be present in BGP RIB and FIB
+ Default Route from R3 Should be present only in BGP RIB not in FIB
+ """
+ step(STEP)
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ static_routes_input,
+ next_hop=DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ expected=False,
+ )
+ assert result is not True, "Testcase {} : Failed {} \n Error: {}".format(
+ tc_name, STEP, result
+ )
+
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R3[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove import VRF configure in step 8 and then remove import VRF configured on step 9"
+ )
+ local_as = get_dut_as_number(tgen, "r2")
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "RED", "delete": True}}},
+ "ipv6": {"unicast": {"import": {"vrf": "RED", "delete": True}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the routes imported from non default VRF - RED is removed")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R3_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ },
+ {
+ "network": [R3_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R3_NETWORK_CONNECTED_NXTHOP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n routes imported from non default VRF is not expected Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n routes imported from non default VRF is not expected \nError: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove import VRF configure in step 8 and then remove import VRF configured on step 9"
+ )
+ local_as = "2000"
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"import": {"vrf": "default", "delete": True}}
+ },
+ "ipv6": {
+ "unicast": {"import": {"vrf": "default", "delete": True}}
+ },
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Verify that the routes impoted from default VRF is removed")
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R1_NETWORK_LOOPBACK[addr_type]],
+ "next_hop": R1_NETWORK_LOOPBACK_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [R1_NETWORK_CONNECTED[addr_type]],
+ "next_hop": R1_NETWORK_CONNECTED_NXTHOP[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n routes impoted from default VRF is not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n routes impoted from default VRF is not expected \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_default_originate_route_with_non_default_VRF_with_route_map_p1(request):
+ """
+ "Verify default-originate route with non-default VRF with route-map import "
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step("Configure IPV4 and IPV6 static route on R0 with Null nexthop")
+ STEP = """
+ Configure IPV4 and IPV6 EBGP session between R0 and R1
+ Configure IPV4 and IPV6 static route on R0 with Null nexthop """
+ step(STEP)
+ input_dict = {
+ "r0": {"bgp": {"local_as": 222, "vrf": "default"}},
+ "r1": {"bgp": {"local_as": 333, "vrf": "default"}},
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configuring static route at R0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK5_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(" Configure re-distribute static on R0 for R0 to R1 for IPV4 and IPV6 peer ")
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure default-originate on R1 for R1 to R2 neighbor for IPv4 and IPv6 peer"
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"default_originate": {"r2": {}}}},
+ "ipv6": {"unicast": {"default_originate": {"r2": {}}}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ snapshot1 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED")
+
+ step("Verify IPv4 and IPv6 static received on R2 default VRF as BGP routes")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK5_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ " Configure IPv4 and IPv6 prefix-list of of route received from R1 on R2 and for 0.0.0.0/0 0::0/0 route"
+ )
+ input_dict_3 = {
+ "r2": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK5_1["ipv4"],
+ "action": "permit",
+ },
+ {"seqid": "2", "network": "0.0.0.0/0", "action": "permit"},
+ {
+ "seqid": "3",
+ "network": NETWORK2_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK5_1["ipv6"],
+ "action": "permit",
+ },
+ {"seqid": "2", "network": "0::0/0", "action": "permit"},
+ {
+ "seqid": "3",
+ "network": NETWORK2_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r2": {"prefix_lists": ["Pv4", "Pv6"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IPv4/IPv6 route-map on R2 with deny sequence using above prefix-list"
+ )
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "deny",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ STEP = """
+ import Route-map anf non-default VRF into defailt vrf
+ import vrf route-map RM1
+ import vrf red
+ """
+ step(STEP)
+
+ local_as = get_dut_as_number(tgen, "r2")
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "RED"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "RED"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(STEP)
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {"unicast": {"import": {"vrf": "route-map RMv4"}}},
+ "ipv6": {"unicast": {"import": {"vrf": "route-map RMv6"}}},
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify IPv4 and IPv6 routes present on VRF red ( static , default-originate) should not get advertised to default VRF "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n VRF red ( static , default-originate) should not get advertised to default VRF \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes_input, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n VRF red ( static , default-originate) should not get advertised to default VRF \nError: {}".format(
+ tc_name, result
+ )
+
+ step("Change route-map sequence deny to permit")
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 and IPv6 routes present on VRF red ( static , default-originate) should get advertised to default VRF"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [DEFAULT_ROUTES[addr_type]],
+ "next_hop": DEFAULT_ROUTE_NXT_HOP_R1[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify Out-prefix count incremented for IPv4/IPv6 default route on VRF red")
+ snapshot2 = get_prefix_count_route(tgen, topo, dut="r2", peer="r3", vrf="RED")
+ step("verifying the prefix count incrementing or not ")
+ isIPv4prefix_incremented = False
+ isIPv6prefix_incremented = False
+ if snapshot1["ipv4_count"] <= snapshot2["ipv4_count"]:
+ isIPv4prefix_incremented = True
+ if snapshot1["ipv6_count"] <= snapshot2["ipv6_count"]:
+ isIPv6prefix_incremented = True
+
+ assert (
+ isIPv4prefix_incremented is True
+ ), "Testcase {} : Failed Error: IPV4 Prefix is not incremented on receiveing ".format(
+ tc_name
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py b/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py
new file mode 100644
index 0000000..9e3a3b5
--- /dev/null
+++ b/tests/topotests/bgp_default_originate/test_default_originate_conditional_routemap.py
@@ -0,0 +1,2104 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2022 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.
+# Shreenidhi A R <rshreenidhi@vmware.com>
+# 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 scenerios are covered.
+1. When there is change in route-map policy associated with default-originate, changes does not reflect.
+2. When route-map associated with default-originate is deleted, default route doesn't get withdrawn
+3. Update message is not being sent when only route-map is removed from the default-originate config.
+4. SNT counter gets incremented on change of every policy associated with default-originate
+5. Route-map with multiple match clauses causes inconsistencies with default-originate.
+6. BGP-Default originate behaviour with BGP attributes
+"""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+from lib.topolog import logger
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ get_prefix_count_route,
+ modify_as_number,
+ verify_bgp_rib,
+ get_dut_as_number,
+ verify_rib_default_route,
+ verify_fib_default_route,
+)
+from lib.common_config import (
+ verify_fib_routes,
+ step,
+ required_linux_kernel_version,
+ create_route_maps,
+ interface_status,
+ create_prefix_lists,
+ get_frr_ipv6_linklocal,
+ start_topology,
+ write_test_header,
+ verify_prefix_lists,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_static_routes,
+ check_router_status,
+ delete_route_maps,
+)
+
+# 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/"))
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Required to instantiate the topology builder class.
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+
+# Global variables
+topo = None
+NETWORK1_1 = {"ipv4": "198.51.1.1/32", "ipv6": "2001:DB8::1:1/128"}
+DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_default_originate_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ global DEFAULT_ROUTES
+ global DEFAULT_ROUTE_NXT_HOP_R1, DEFAULT_ROUTE_NXT_HOP_R3
+ 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
+ )
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ interface = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r1", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r1"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R1 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ interface = topo["routers"]["r3"]["links"]["r2"]["interface"]
+ ipv6_link_local = get_frr_ipv6_linklocal(tgen, "r3", intf=interface)
+ ipv4_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv4"].split("/")[0]
+ ipv6_nxt_hop = topo["routers"]["r3"]["links"]["r2"]["ipv6"].split("/")[0]
+ DEFAULT_ROUTE_NXT_HOP_R3 = {"ipv4": ipv4_nxt_hop, "ipv6": ipv6_link_local}
+
+ 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 test_default_originate_delete_conditional_routemap(request):
+ """
+ "scenerio covered":
+ 1. When there is change in route-map policy associated with default-originate, changes does not reflect.
+ 2. When route-map associated with default-originate is deleted, default route doesn't get withdrawn
+ 3. Update message is not being sent when only route-map is removed from the default-originate config.
+ 4. SNT counter gets incremented on change of every policy associated with default-originate
+ 5. Route-map with multiple match clauses causes inconsistencies with default-originate.
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , IBGP neighbor between R1 and R2")
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R1 and R0")
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": 999,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": 1000,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 2000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 3000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+
+ step("After changing the BGP remote as , Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert (
+ BGP_CONVERGENCE is True
+ ), "Complete convergence is expected after changing ASN ....! ERROR :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step("Configure 1 IPv4 and 1 IPv6 Static route on R0 with next-hop as Null0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the static route \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are configured and up on R0")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r0", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : routes {} not found in R0 FIB \n Error: {}".format(
+ tc_name, static_routes_input, result
+ )
+
+ step(
+ "Configure redistribute static on IPv4 and IPv6 address family on R0 for R0 to R1 neighbor "
+ )
+ redistribute_static = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure redistribute configuration....! \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify IPv4 and IPv6 static route are received on R1")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r1", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed... Routes {} expected in r1 FIB after configuring the redistribute config on R0 \n Error: {}".format(
+ tc_name, static_routes_input, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", static_routes_input)
+ assert (
+ result is True
+ ), "Testcase {} : Failed... Routes {} expected in r1 RIB after configuring the redistribute config on R0\n Error: {}".format(
+ tc_name, static_routes_input, result
+ )
+
+ step(
+ "Configure IPv4 prefix-list 'Pv4' and and IPv6 prefix-list 'Pv6' on R1 to match BGP route Sv41, IPv6 route Sv61 permit "
+ )
+ input_dict_3 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv4": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ },
+ ]
+ },
+ "ipv6": {
+ "Pv6": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ },
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the prefix list \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure IPV4 and IPv6 route-map (RMv4 and RMv6) matching prefix-list (Pv4 and Pv6) respectively on R1"
+ )
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ "set": {
+ "path": {
+ "as_num": "5555",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ "set": {
+ "path": {
+ "as_num": "5555",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure default-originate with route-map (RMv4 and RMv6) on R1, on BGP IPv4 and IPv6 address family "
+ )
+ local_as = get_dut_as_number(tgen, dut="r1")
+ default_originate_config = {
+ "r1": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the default originate \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring default-originate command , verify default routes are advertised on R2 "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ expected_aspath="5555",
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the default originate \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the default originate \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Changing the as-path policy of the existing route-map")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ "set": {
+ "path": {
+ "as_num": "6666",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ "set": {
+ "path": {
+ "as_num": "6666",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify prefix sent count on R1 towards R2 \n Send count shoud not be incremented on change of existing (AS-path) policy "
+ )
+ snapshot = get_prefix_count_route(
+ tgen, topo, dut="r1", peer="r2", link="r1", sent=True, received=False
+ )
+
+ ipv4_prefix_count = False
+ ipv6_prefix_count = False
+ if snapshot["ipv4_count"] == 2:
+ ipv4_prefix_count = True
+ if snapshot["ipv6_count"] == 2:
+ ipv6_prefix_count = True
+
+ assert (
+ ipv4_prefix_count is True
+ ), "Testcase {} : Failed Error: Expected sent Prefix is 2 but obtained {} ".format(
+ tc_name, ipv4_prefix_count
+ )
+ assert (
+ ipv6_prefix_count is True
+ ), "Testcase {} : Failed Error: Expected sent Prefix is 2 but obtained {} ".format(
+ tc_name, ipv6_prefix_count
+ )
+
+ step(
+ "After changing the as-path policy verify the new policy is advertised to router R2"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ expected_aspath="6666",
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Default route with expected attributes is not found in BGP RIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Default route with expected attributes is not found in BGP FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the as-path policy from the route-map")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ "set": {
+ "path": {
+ "as_num": "6666",
+ "as_action": "prepend",
+ "delete": True,
+ }
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ "set": {
+ "path": {
+ "as_num": "6666",
+ "as_action": "prepend",
+ "delete": True,
+ }
+ },
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the route policy (AS-Path) verify that as-path is removed in r2 "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ )
+ assert result is True, "Testcase {} : Failed ... ! \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed .... !\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete the route-map ")
+
+ delete_routemap = {"r1": {"route_maps": ["RMv4", "RMv6"]}}
+ result = delete_route_maps(tgen, delete_routemap)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to delete the route-map\n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After deleting route-map , verify the default route in FIB and RIB are removed "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : After removing the route-map the default-route is not removed from R2 RIB\n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : After removing the route-map the default-route is not removed from R2 FIB \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-map with with sequnce number 10 ")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ "set": {
+ "path": {
+ "as_num": "9999",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ "set": {
+ "path": {
+ "as_num": "9999",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After Configuring the route-map the dut is expected to receive the route policy (as-path) as 99999"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ expected_aspath="9999",
+ )
+ assert result is True, "Testcase {} : Failed...! \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert result is True, "Testcase {} : Failed ...!\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create another route-map with seq number less than the previous i. <10 ")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {"ipv4": {"prefix_lists": "Pv4"}},
+ "set": {
+ "path": {
+ "as_num": "7777",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {"ipv6": {"prefix_lists": "Pv6"}},
+ "set": {
+ "path": {
+ "as_num": "7777",
+ "as_action": "prepend",
+ }
+ },
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert (
+ result is True
+ ), "Testcase {} : Failed to configure the route map \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On creating new route-map the route-map with lower seq id should be considered "
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ metric=0,
+ expected_aspath="7777",
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Route-map with lowest prefix is not considered \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R1,
+ expected=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Route-map with lowest prefix is not considered \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_default_originate_after_BGP_attributes_p1(request):
+ """
+ "Verify different BGP attributes with default-originate route "
+ """
+ tgen = get_topogen()
+ global BGP_CONVERGENCE
+ global topo
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ if BGP_CONVERGENCE != True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ step("Configure IPv4 and IPv6 , EBGP neighbor between R3 and R2")
+ step("Configure IPv4 and IPv6 IBGP neighbor between R3 and R4")
+ r0_local_as = topo['routers']['r0']['bgp']['local_as']
+ r1_local_as = topo['routers']['r1']['bgp']['local_as']
+ r2_local_as = topo['routers']['r2']['bgp']['local_as']
+ r3_local_as = topo['routers']['r3']['bgp']['local_as']
+ r4_local_as = topo['routers']['r4']['bgp']['local_as']
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": r1_local_as,
+ }
+ },
+ "r2": {
+ "bgp": {
+ "local_as": r2_local_as,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 4000,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ step(
+ "Configure one IPv4 and one IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, IPv6 route Sv61 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes configured on R4 in FIB")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute static knob on R4 , for R4 to R3 neighbor for IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify After configuring redistribute static , verify route received in BGP table of R3"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ NOTE = """Configure 2 IPv4 prefix-list Pv41 Pv42 and and 2 IPv6 prefix-list Pv61 Pv62 on R3 to match BGP IPv4 route Sv41, 200.1.1.1/24 , IPv6 route Sv61 and 200::1/64"""
+ step(NOTE)
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "Pv41": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv4"],
+ "action": "permit",
+ }
+ ],
+ "Pv42": [
+ {"seqid": "1", "network": "200.1.1.1/24", "action": "permit"}
+ ],
+ },
+ "ipv6": {
+ "Pv61": [
+ {
+ "seqid": "1",
+ "network": NETWORK1_1["ipv6"],
+ "action": "permit",
+ }
+ ],
+ "Pv62": [
+ {"seqid": " 1", "network": "200::1/64", "action": "permit"}
+ ],
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify IPv4 and IPv6 Prefix list got configured on R3")
+ input_dict = {"r3": {"prefix_lists": ["Pv41", "Pv61", "Pv42", "Pv62"]}}
+ result = verify_prefix_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure 2 sequence of route-map for IPv4 seq1 permit Pv41 and seq2 permit Pv42 and for IPv6 seq1 permit Pv61 , seq2 permit Pv62 on R3"
+ )
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv4": {"prefix_lists": "Pv41"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "match": {"ipv4": {"prefix_lists": "Pv42"}},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "Pv61"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "match": {"ipv6": {"prefix_lists": "Pv62"}},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Apply on route-map seq1 set as-path prepend to 200 and route-map seq2 set as-path prepend to 300 for IPv4 and IPv6 route-map "
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "path": {
+ "as_num": "200",
+ "as_action": "prepend",
+ }
+ }
+
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "300",
+ "as_action": "prepend",
+ }
+ }
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "path": {
+ "as_num": "200",
+ "as_action": "prepend",
+ }
+ }
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "300",
+ "as_action": "prepend",
+ }
+ }
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ " Configure default-originate with IPv4 and IPv6 route-map on R3 for R3-R2 IPv4 and IPv6 BGP neighbor"
+ )
+
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 and IPv6 default route received on R2 with both the AS path on R2"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=0,
+ expected_aspath="4000 200",
+ )
+
+ step(
+ "Modify AS prepend path adding one more value 500 in route-map sequence 1 and 600 for route-map sequence 2 for IPv4 and IPv6 route-map"
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "path": {
+ "as_num": "500",
+ "as_action": "prepend",
+ }
+ }
+
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "600",
+ "as_action": "prepend",
+ }
+ }
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "path": {
+ "as_num": "500",
+ "as_action": "prepend",
+ }
+ }
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "600",
+ "as_action": "prepend",
+ }
+ }
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+
+ step("As path 500 added to IPv4 and IPv6 default -originate route received on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=0,
+ expected_aspath="4000 500",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Apply on route-map seq1 set metric value to 70 and route-map seq2 set metric 80 IPv4 and IPv6 route-map"
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "metric": 70,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "metric": 80,
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "metric": 70,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "metric": 80,
+ },
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes "
+ )
+
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=70,
+ expected_aspath="4000 500",
+ )
+
+
+ step(
+ "Modify route-map seq1 configure metric 50 and route-map seq2 configure metric 100 IPv4 and IPv6 route-map "
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "metric": 50,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "metric": 100,
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "metric": 50,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "metric": 100,
+ },
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes "
+ )
+
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=50,
+ expected_aspath="4000 500",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete AS-prepend from IP4 and IPv6 route-map configured on R3 ")
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+
+ "set": {
+ "path": {
+ "as_num": "500",
+ "as_action": "prepend",
+ "delete": True,
+ },
+ "delete": True,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "600",
+ "as_action": "prepend",
+ "delete": True,
+ },
+ "delete": True,
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "path": {
+ "as_num": "500",
+ "as_action": "prepend",
+ "delete": True,
+ },
+ "delete": True,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "path": {
+ "as_num": "600",
+ "as_action": "prepend",
+ "delete": True,
+ },
+ "delete": True,
+ },
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify AS-prepend is deleted from default originate route and metric value only present on R2 for IPv4 and IPv6 default routes "
+ )
+
+
+
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=50,
+ expected_aspath="4000",
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+
+ step("Delete metric value from IP4 and IPv6 route-map configured on R3 ")
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {"metric": 50, "delete": True},
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {"metric": 100, "delete": True},
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {"metric": 50, "delete": True},
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {"metric": 100, "delete": True},
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Metric value deleted from IPv4 and IPv6 default route on R2 ,verify default routes "
+ )
+
+
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ metric=0,
+ expected_aspath="4000",
+ )
+
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ step("Change IPv4 and IPv6 , EBGP to IBGP neighbor between R3 and R2")
+ step("Change IPv4 and IPv6 IBGP to EBGP neighbor between R3 and R4")
+ r0_local_as = topo['routers']['r0']['bgp']['local_as']
+ r1_local_as = topo['routers']['r1']['bgp']['local_as']
+ r2_local_as = topo['routers']['r2']['bgp']['local_as']
+ r3_local_as = topo['routers']['r3']['bgp']['local_as']
+ r4_local_as = topo['routers']['r4']['bgp']['local_as']
+ input_dict = {
+ "r0": {
+ "bgp": {
+ "local_as": r0_local_as,
+ }
+ },
+ "r1": {
+ "bgp": {
+ "local_as": r1_local_as,
+ }
+ },
+
+ "r2": {
+ "bgp": {
+ "local_as": 1111,
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": 1111,
+ }
+ },
+ "r4": {
+ "bgp": {
+ "local_as": 5555,
+ }
+ },
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+ try:
+ assert result is True
+ except AssertionError:
+ logger.info("Expected behaviour: {}".format(result))
+ logger.info("BGP config is not created because of invalid ASNs")
+ step("After changing the BGP AS Path Verify the BGP Convergence")
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+ step(
+ "Configure one IPv4 and one IPv6, Static route on R4 with next-hop as Null0 IPv4 route Sv41, IPv6 route Sv61 "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes configured on R4 in FIB")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure redistribute static knob on R4 , for R4 to R3 neighbor for IPv4 and IPv6 address family "
+ )
+ redistribute_static = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, redistribute_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify After configuring redistribute static , verify route received in BGP table of R3"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ " Configure default-originate with IPv4 and IPv6 route-map on R3 for R3-R2 IPv4 and IPv6 BGP neighbor"
+ )
+ local_as = get_dut_as_number(tgen, dut="r3")
+ default_originate_config = {
+ "r3": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv4"}}}
+ },
+ "ipv6": {
+ "unicast": {"default_originate": {"r2": {"route_map": "RMv6"}}}
+ },
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, default_originate_config)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 and IPv6 default route received on R2 with both the AS path on R2"
+ )
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "0::0/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure local -preference to 50 on IPv4 and IPv6 route map seq1 and 60 on seq2"
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "locPrf": 50,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "locPrf": 60,
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "locPrf": 50,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "locPrf": 60,
+ },
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Configured metric value received on R2 along with as-path for IPv4 and IPv6 default routes "
+ )
+
+
+
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ locPrf=50,
+ )
+
+
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Modify local preference value to 150 on IPv4 and IPv6 route map seq1 and 160 on seq2"
+ )
+ route_map = {
+ "r3": {
+ "route_maps": {
+ "RMv4": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "locPrf": 150,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "locPrf": 160,
+ },
+ },
+ ],
+ "RMv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {
+ "locPrf": 150,
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": "2",
+ "set": {
+ "locPrf": 160,
+ },
+ },
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify Modified local-preference value received on R2 for IPv4 and IPv6 default routes "
+ )
+
+
+
+
+ DEFAULT_ROUTES = {"ipv4": "0.0.0.0/0", "ipv6": "::/0"}
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ locPrf=150,
+ )
+
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ # updating the topology with the updated AS-Number to avoid conflict in con configuring the AS
+ updated_topo = topo
+ updated_topo['routers']['r0']['bgp']['local_as']=get_dut_as_number(tgen,"r0")
+ updated_topo['routers']['r1']['bgp']['local_as']=get_dut_as_number(tgen,"r1")
+ updated_topo['routers']['r2']['bgp']['local_as']=get_dut_as_number(tgen,"r2")
+ updated_topo['routers']['r3']['bgp']['local_as']=get_dut_as_number(tgen,"r3")
+ updated_topo['routers']['r4']['bgp']['local_as']=get_dut_as_number(tgen,"r4")
+
+ step("Shut IPv4/IPv6 BGP neighbor from R4 ( R4-R3) using 'neighbor x.x.x.x shut' command ")
+ local_as = get_dut_as_number(tgen, dut="r4")
+ shut_neighbor = {
+ "r4": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"shutdown":True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"shutdown":True}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo['routers']['r3']['links']['r4']['interface']
+ input_dict = {
+ "r1": {
+ "interface_list": [interface],
+ "status": "down"
+ }
+ }
+
+ result = interface_status(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Shut down the interface failed ! \n Error: {}".format(tc_name, result)
+
+ step("After shutting the interface verify the BGP convergence")
+ result = verify_bgp_convergence(tgen,topo,expected=False)
+ assert result is not True, "Testcase {} : Failed \n After shutting Down BGP convergence should Fail and return False \n Error: {}".format(tc_name, result)
+
+ step("verify default route deleted from R2 ")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: After Shut down interface the default route is NOT expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: After Shut down interface the default route is NOT expected but found in FIB -> {}".format( tc_name, result)
+
+
+ step("no Shut IPv4/IPv6 BGP neighbor from R4 ( R4-R3) using 'neighbor x.x.x.x shut' command ")
+ local_as = get_dut_as_number(tgen, dut="r4")
+ shut_neighbor = {
+ "r4": {
+ "bgp": {
+ "local_as": local_as,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"shutdown":False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"shutdown":False}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, shut_neighbor)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ interface = topo['routers']['r3']['links']['r4']['interface']
+ input_dict = {
+ "r1": {
+ "interface_list": [interface],
+ "status": "up"
+ }
+ }
+
+ result = interface_status(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Bring up interface failed ! \n Error: {}".format(tc_name, result)
+
+ step("After no shutting the interface verify the BGP convergence")
+ result = verify_bgp_convergence(tgen,topo,expected=True)
+ assert result is True, "Testcase {} : Failed \n After shutting Down BGP convergence should Fail and return False \n Error: {}".format(tc_name, result)
+
+ step("After no shut neighbor , verify default route relearn on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True)
+ assert result is True, "Testcase {} : Failed \n Error: After no Shut down interface the default route is expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected=True)
+ assert result is True, "Testcase {} : Failed \n Error: After Shut down interface the default route is expected but found in FIB -> {}".format( tc_name, result)
+
+
+
+ step("Remove IPv4/IPv6 static route configure on R4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "delete": True
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes removed on R4 in FIB")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes_input, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("After removing static route , verify default route removed on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= False)
+ assert result is not True, "Testcase {} : Failed \n Error: After removing static the default route is NOT expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= False)
+ assert result is not True, "Testcase {} : Failed \n Error: After removing static the default route is NOT expected but found in FIB -> {}".format( tc_name, result)
+
+
+ step("Configuring the static route back in r4")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Verify IPv4 and IPv6 static routes configured on R4 in FIB")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_fib_routes(tgen, addr_type, "r4", static_routes_input, expected=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes_input, expected=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("After adding static route back , verify default route learned on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= True)
+ assert result is True, "Testcase {} : Failed \n Error: After removing static the default route is expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= True)
+ assert result is True, "Testcase {} : Failed \n Error: After removing static the default route is expected but found in FIB -> {}".format( tc_name, result)
+
+ step("Deactivate IPv4 and IPv6 neighbor configured from R4 ( R4-R3)")
+
+ configure_bgp_on_r1 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {"deactivate": "ipv4"}}}
+ }
+ },
+
+ },"ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {"deactivate": "ipv6"}}}
+ }
+ },
+
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("After deactivating the BGP neighbor , verify default route removed on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= False)
+ assert result is not True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is NOT expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= False)
+ assert result is not True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is NOT expected but found in FIB -> {}".format( tc_name, result)
+
+ step("Activate IPv4 and IPv6 neighbor configured from R4 ( R4-R3)")
+
+ configure_bgp_on_r1 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {"activate": "ipv4"}}}
+ }
+ },
+
+ },"ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r4": {"activate": "ipv6"}}}
+ }
+ },
+
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, updated_topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp convergence.")
+ bgp_convergence = verify_bgp_convergence(tgen, updated_topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+ step("After Activating the BGP neighbor , verify default route learned on R2")
+ result = verify_rib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= True)
+ assert result is True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in RIB -> {}".format( tc_name, result)
+
+ result = verify_fib_default_route(
+ tgen,
+ topo,
+ dut="r2",
+ routes=DEFAULT_ROUTES,
+ expected_nexthop=DEFAULT_ROUTE_NXT_HOP_R3,
+ expected= True)
+ assert result is True, "Testcase {} : Failed \n Error: After Deactivating the BGP neighbor the default route is expected but found in FIB -> {}".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/bgp_default_route/__init__.py b/tests/topotests/bgp_default_route/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_route/__init__.py
diff --git a/tests/topotests/bgp_default_route/r1/bgpd.conf b/tests/topotests/bgp_default_route/r1/bgpd.conf
new file mode 100644
index 0000000..8699d62
--- /dev/null
+++ b/tests/topotests/bgp_default_route/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route/r1/zebra.conf b/tests/topotests/bgp_default_route/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_default_route/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route/r2/bgpd.conf b/tests/topotests/bgp_default_route/r2/bgpd.conf
new file mode 100644
index 0000000..6d1080c
--- /dev/null
+++ b/tests/topotests/bgp_default_route/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_default_route/r2/zebra.conf b/tests/topotests/bgp_default_route/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_default_route/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route/test_bgp_default-originate.py b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
new file mode 100644
index 0000000..b2d530b
--- /dev/null
+++ b/tests/topotests/bgp_default_route/test_bgp_default-originate.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works without route-map.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_if_received():
+ output = json.loads(
+ tgen.gears["r2"].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_if_originated():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp summary json"))
+ expected = {"ipv4Unicast": {"peers": {"192.168.255.2": {"pfxSnt": 1}}}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_is_valid(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {"paths": [{"valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_check_if_received)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 at r2 from r1"
+
+ test_func = functools.partial(_bgp_check_if_originated)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "No 0.0.0.0/0 from r1 to r2"
+
+ test_func = functools.partial(_bgp_default_route_is_valid, tgen.gears["r2"])
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see 0.0.0.0/0 in r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_match/__init__.py b/tests/topotests/bgp_default_route_route_map_match/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/__init__.py
diff --git a/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf
new file mode 100644
index 0000000..97b440f
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/r1/bgpd.conf
@@ -0,0 +1,17 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ network 192.168.13.0/24 route-map internal
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+bgp community-list standard default seq 5 permit 65000:1
+!
+route-map default permit 10
+ match community default
+!
+route-map internal permit 10
+ set community 65000:1
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf
new file mode 100644
index 0000000..00c96cc
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
new file mode 100644
index 0000000..11eaa7b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match/test_bgp_default-originate_route-map_match.py
@@ -0,0 +1,110 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works with ONLY match operations.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_is_valid(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {"paths": [{"valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Failed to see applied metric for default route in "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_match2/__init__.py b/tests/topotests/bgp_default_route_route_map_match2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/__init__.py
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf
new file mode 100644
index 0000000..ee7a92f
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r1/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+ip prefix-list r2 permit 10.0.0.0/22
+!
+route-map default permit 10
+ match ip address prefix-list r2
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf
new file mode 100644
index 0000000..e2c399e
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf
new file mode 100644
index 0000000..00c96cc
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf
new file mode 100644
index 0000000..f355ab1
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 10.0.0.1/22
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
new file mode 100644
index 0000000..99528f6
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match2/test_bgp_default-originate_route-map_match2.py
@@ -0,0 +1,134 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works with conditional match.
+If 10.0.0.0/22 is recived from r2, then we announce 0.0.0.0/0
+to r2.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_is_valid(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {"paths": [{"valid": True}]}
+ return topotest.json_cmp(output, expected)
+
+ step("Converge network")
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see bgp convergence at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+ step("Announce 10.0.0.0/22 from R2")
+ router.vtysh_cmd("conf t\nrouter bgp\naddress-family ipv4\nredistribute connected")
+
+ step("Check if we have 0.0.0.0/0 at R2")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "0.0.0.0/0 does not exist at r2"
+
+ step("Withdraw 10.0.0.0/22 from R2 again")
+ router.vtysh_cmd(
+ "conf t\nrouter bgp\naddress-family ipv4\nno redistribute connected"
+ )
+
+ step("Check if we don't have 0.0.0.0/0 at R2 again")
+ test_func = functools.partial(_bgp_default_route_is_valid, router)
+ success, result = topotest.run_and_expect(test_func, not None, count=30, wait=0.5)
+ assert result is not None, "0.0.0.0/0 exists at r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/__init__.py b/tests/topotests/bgp_default_route_route_map_match_set/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/__init__.py
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf
new file mode 100644
index 0000000..32ac7c5
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ network 192.168.13.0/24 route-map internal
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+bgp community-list standard default seq 5 permit 65000:1
+!
+route-map default permit 10
+ match community default
+ set metric 123
+ set as-path prepend 65000 65000 65000
+!
+route-map internal permit 10
+ set community 65000:1
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf
new file mode 100644
index 0000000..00c96cc
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
new file mode 100644
index 0000000..c890b0d
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_match_set/test_bgp_default-originate_route-map_match_set.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works with match operations.
+And verify if set operations work as well.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_has_metric(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {"string": "65000 65000 65000 65000"},
+ "metric": 123,
+ "community": None,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_default_route_has_metric, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Failed to see applied metric for default route in "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_default_route_route_map_set/__init__.py b/tests/topotests/bgp_default_route_route_map_set/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/__init__.py
diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf
new file mode 100644
index 0000000..6f6d394
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 default-originate route-map default
+ exit-address-family
+!
+route-map default permit 10
+ set metric 123
+ set as-path prepend 65000 65000 65000
+!
diff --git a/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf
new file mode 100644
index 0000000..00c96cc
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
new file mode 100644
index 0000000..cc2243a
--- /dev/null
+++ b/tests/topotests/bgp_default_route_route_map_set/test_bgp_default-originate_route-map_set.py
@@ -0,0 +1,112 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if default-originate works with ONLY set operations.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_default_originate_route_map():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 1}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_default_route_has_metric(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 0.0.0.0/0 json"))
+ expected = {
+ "paths": [{"aspath": {"string": "65000 65000 65000 65000"}, "metric": 123}]
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed to see bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_default_route_has_metric, router)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Failed to see applied metric for default route in "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_disable_addpath_rx/__init__.py b/tests/topotests/bgp_disable_addpath_rx/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/__init__.py
diff --git a/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf
new file mode 100644
index 0000000..af1353e
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r1/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 65001
+ timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers connect 5
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 disable-addpath-rx
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf
new file mode 100644
index 0000000..db68e55
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r2/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65002
+ timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers connect 5
+ neighbor 192.168.2.3 remote-as external
+ neighbor 192.168.2.3 timers connect 5
+ neighbor 192.168.2.4 remote-as external
+ neighbor 192.168.2.4 timers connect 5
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 addpath-tx-all-paths
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf
new file mode 100644
index 0000000..e4a9074
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
+int r2-eth1
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf
new file mode 100644
index 0000000..3ac6a08
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r3/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65003
+ timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf
new file mode 100644
index 0000000..417a484
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r3-eth0
+ ip address 192.168.2.3/24
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf
new file mode 100644
index 0000000..8ab405f
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r4/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65004
+ timers 3 10
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers connect 5
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf b/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf
new file mode 100644
index 0000000..241e386
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/r4/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.254/32
+!
+int r4-eth0
+ ip address 192.168.2.4/24
+!
diff --git a/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py b/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py
new file mode 100644
index 0000000..ed88d5d
--- /dev/null
+++ b/tests/topotests/bgp_disable_addpath_rx/test_disable_addpath_rx.py
@@ -0,0 +1,142 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if AddPath RX direction is not negotiated via AddPath capability.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_disable_addpath_rx():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ step(
+ "Check if r2 advertised only 2 paths to r1 (despite addpath-tx-all-paths enabled on r2)."
+ )
+
+ def check_bgp_advertised_routes(router):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.1 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "172.16.16.254/32": {
+ "addrPrefix": "172.16.16.254",
+ "prefixLen": 32,
+ },
+ "192.168.2.0/24": {
+ "addrPrefix": "192.168.2.0",
+ "prefixLen": 24,
+ },
+ },
+ "totalPrefixCounter": 2,
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(check_bgp_advertised_routes, r2)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "AddPath TX not working."
+
+ step("Check if AddPath RX is disabled on r1 and we receive only 2 paths.")
+
+ def check_bgp_disabled_addpath_rx(router):
+ output = json.loads(router.vtysh_cmd("show bgp neighbor 192.168.1.2 json"))
+ expected = {
+ "192.168.1.2": {
+ "bgpState": "Established",
+ "neighborCapabilities": {
+ "addPath": {
+ "ipv4Unicast": {"txReceived": True, "rxReceived": True}
+ },
+ },
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(check_bgp_disabled_addpath_rx, r1)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "AddPath RX advertised, but should not."
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_distance_change/__init__.py b/tests/topotests/bgp_distance_change/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/__init__.py
diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist.json b/tests/topotests/bgp_distance_change/bgp_admin_dist.json
new file mode 100755
index 0000000..e6a20a6
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/bgp_admin_dist.json
@@ -0,0 +1,402 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "192.168.22.1/32",
+ "no_of_ip": 2,
+ "next_hop": "10.0.0.2"
+ },
+ {
+ "network": "fc07:1::1/128",
+ "no_of_ip": 2,
+ "next_hop": "fd00::2"
+ },
+ {
+ "network": "192.168.21.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ },
+ {
+ "network": "fc07:150::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "192.168.20.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ },
+ {
+ "network": "fc07:50::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ },
+ {
+ "network": "192.168.21.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ },
+ {
+ "network": "fc07:150::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole"
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
new file mode 100755
index 0000000..23afa2c
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/bgp_admin_dist_vrf.json
@@ -0,0 +1,429 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [{
+ "local_as": "100",
+ "vrf": "RED",
+
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ }],
+ "static_routes": [
+ {
+ "network": "192.168.22.1/32",
+ "no_of_ip": 2,
+ "next_hop": "10.0.0.2",
+ "vrf": "RED"
+ },
+ {
+ "network": "fc07:1::1/128",
+ "no_of_ip": 2,
+ "next_hop": "fd00::2",
+ "vrf": "RED"
+ },
+ {
+ "network": "192.168.21.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ },
+ {
+ "network": "fc07:150::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [{
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [{
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }]
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [{
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }]
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback",
+ "vrf": "RED"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [{
+ "local_as": "300",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ }],
+ "static_routes": [
+ {
+ "network": "192.168.20.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ },
+ {
+ "network": "fc07:50::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ },
+ {
+ "network": "192.168.21.1/32",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ },
+ {
+ "network": "fc07:150::1/128",
+ "no_of_ip": 2,
+ "next_hop": "blackhole",
+ "vrf": "RED"
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_distance_change/r1/bgpd.conf b/tests/topotests/bgp_distance_change/r1/bgpd.conf
new file mode 100644
index 0000000..07cfe2e
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/r1/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_distance_change/r1/zebra.conf b/tests/topotests/bgp_distance_change/r1/zebra.conf
new file mode 100644
index 0000000..6e9b0b4
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_distance_change/r2/bgpd.conf b/tests/topotests/bgp_distance_change/r2/bgpd.conf
new file mode 100644
index 0000000..9cd86dc
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_distance_change/r2/zebra.conf b/tests/topotests/bgp_distance_change/r2/zebra.conf
new file mode 100644
index 0000000..93e3590
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py b/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py
new file mode 100755
index 0000000..90c3d22
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist.py
@@ -0,0 +1,1282 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 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.
+#
+
+import sys
+import time
+import pytest
+import inspect
+import os
+
+
+"""Following tests are covered to test bgp admin distance functionality.
+TC_1:
+ Verify bgp admin distance functionality when static route is
+ configured same as ebgp learnt route
+
+TC_2:
+ Verify ebgp admin distance functionality with ECMP.
+
+TC_3:
+ Verify ibgp admin distance functionality when static route is
+ configured same as bgp learnt route.
+TC_4:
+ Verify ibgp admin distance functionality with ECMP.
+
+TC_7: Chaos - Verify bgp admin distance functionality with chaos.
+"""
+
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +--------- | R2 |
+ | +-------+
+ |iBGP |
+ +-------+ |
+ | R1 | |iBGP
+ +-------+ |
+ | |
+ | iBGP +-------+ eBGP +-------+
+ +---------- | R3 |----------| R4 |
+ +-------+ +-------+
+ |
+ |eBGP
+ |
+ +-------+
+ | R5 |
+ +-------+
+
+
+"""
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ step,
+ write_test_footer,
+ create_static_routes,
+ verify_rib,
+ create_route_maps,
+ create_prefix_lists,
+ check_address_types,
+ reset_config_on_routers,
+ check_router_status,
+ stop_router,
+ kill_router_daemons,
+ start_router_daemons,
+ start_router,
+ get_frr_ipv6_linklocal,
+ verify_fib_routes,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_best_path_as_per_admin_distance,
+ clear_bgp,
+)
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+# Global variables
+topo = None
+bgp_convergence = False
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+NETWORK = {
+ "ipv4": [
+ "192.168.20.1/32",
+ "192.168.20.2/32",
+ "192.168.21.1/32",
+ "192.168.21.2/32",
+ "192.168.22.1/32",
+ "192.168.22.2/32",
+ ],
+ "ipv6": [
+ "fc07:50::1/128",
+ "fc07:50::2/128",
+ "fc07:150::1/128",
+ "fc07:150::2/128",
+ "fc07:1::1/128",
+ "fc07:1::2/128",
+ ],
+}
+
+ADDR_TYPES = check_address_types()
+
+
+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...
+ json_file = "{}/bgp_admin_dist.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # 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)
+
+ # Checking BGP convergence
+ global bgp_convergence
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """teardown_module.
+
+ Teardown the pytest environment.
+ * `mod`: module name
+ """
+ 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)
+
+
+#####################################################
+# Tests starting
+#####################################################
+def test_bgp_admin_distance_ebgp_ecmp_p0():
+ """
+ TC: 2
+ Verify ebgp admin distance functionality with ECMP.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipping test case because of BGP Convergence failure at setup")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure static route in R4 and R5, redistribute in bgp")
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r4": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r5": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that route is learnt in DUT via ebgp")
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ dut = "r3"
+ nhop = {"ipv4": [], "ipv6": []}
+ nhop["ipv4"].append(topo["routers"]["r4"]["links"]["r3"]["ipv4"].split("/")[0])
+ nhop["ipv4"].append(topo["routers"]["r5"]["links"]["r3"]["ipv4"].split("/")[0])
+ nhop["ipv6"].append(get_frr_ipv6_linklocal(tgen, "r4", "r3-r4-eth1"))
+ nhop["ipv6"].append(get_frr_ipv6_linklocal(tgen, "r5", "r1-r3-eth1"))
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure the static route in R3 (Dut).")
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that static route is selected as best route in zebra.")
+
+ # Verifying RIB routes
+ protocol = "static"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step(" Configure the admin distance of 254 to static route in R3.")
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "next_hop": "Null0",
+ "admin_distance": 254,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that bgp routes are selected as best routes in zebra.")
+ protocol = "bgp"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that bgp routes are selected as best routes in zebra.")
+ # Verifying RIB routes
+ protocol = "bgp"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure bgp admin distance 10 with CLI in dut.")
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {"distance": {"ebgp": 10, "ibgp": 254, "local": 254}}
+ },
+ "ipv6": {
+ "unicast": {"distance": {"ebgp": 10, "ibgp": 254, "local": 254}}
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify ebgp routes have admin distance of 10 in dut.")
+
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, admin_distance=10
+ )
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step(
+ "Configure route map with weight as 200 and apply to one of the "
+ "neighbor (R4 neighbor)."
+ )
+
+ # Create Prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_1": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv4"][0],
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_1_ipv6": [
+ {
+ "seqid": 100,
+ "network": NETWORK["ipv6"][0],
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMAP_WEIGHT": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1"}},
+ "set": {"weight": 200},
+ },
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"weight": 200},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that bgp route is selected as best on by zebra in r3.")
+
+ protocol = "bgp"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, admin_distance=10
+ )
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Static route should not be selected as best route.")
+ protocol = "static"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_fib_routes(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result4 is not True
+ ), "Testcase {} : Failed. Wrong route is selected as best route.\n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Reconfigure the static route without admin distance")
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "next_hop": "Null0",
+ "admin_distance": 254,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that static route is installed as best route.")
+ protocol = "static"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, fib=True
+ )
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Unconfigure the static route in R3.")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that bgp route is selected as best on by zebra in r3.")
+
+ protocol = "bgp"
+ dut = "r3"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Un configure the route map on R3.")
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ "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 bgp routes installed in zebra.")
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type][0], "next_hop": "Null0"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_admin_distance_ibgp_p0():
+ """
+ TC: 3
+ Verify bgp admin distance functionality when static route is
+ configured same as ibgp learnt route
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipping test case because of BGP Convergence failure at setup")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure bgp admin distance 200 with CLI in dut.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 200 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "192.168.22.1/32",
+ "admin_distance": 200,
+ },
+ {
+ "network": "192.168.22.2/32",
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "fc07:1::1/128",
+ "admin_distance": 200,
+ },
+ {
+ "network": "fc07:1::2/128",
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Modify the admin distance value to 150.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 150 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "192.168.22.1/32",
+ "admin_distance": 150,
+ },
+ {
+ "network": "192.168.22.2/32",
+ "admin_distance": 150,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "fc07:1::1/128",
+ "admin_distance": 150,
+ },
+ {
+ "network": "fc07:1::2/128",
+ "admin_distance": 150,
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Un configure the admin distance value on DUT")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {
+ "ebgp": 150,
+ "ibgp": 150,
+ "local": 150,
+ "delete": True,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {
+ "ebgp": 150,
+ "ibgp": 150,
+ "local": 150,
+ "delete": True,
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have default admin distance in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "192.168.22.1/32",
+ "admin_distance": 20,
+ },
+ {
+ "network": "192.168.22.2/32",
+ "admin_distance": 20,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": "fc07:1::1/128",
+ "admin_distance": 20,
+ },
+ {
+ "network": "fc07:1::2/128",
+ "admin_distance": 20,
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Learn the same route via ebgp and ibgp peer. Configure admin "
+ "distance of 200 in DUT for both ebgp and ibgp peer. "
+ )
+
+ step("Verify that ebgp route is preferred over ibgp.")
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ input_dict = topo["routers"]
+
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure static route Without any admin distance")
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects static route.")
+ protocol = "static"
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "Null0"}]
+ }
+ }
+
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure static route with admin distance of 253")
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure admin distance of 254 in bgp for route.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that zebra selects static route.")
+ protocol = "static"
+
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Delete the static route.")
+ for addr_type in ADDR_TYPES:
+
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_admin_distance_chaos_p2():
+ """
+ TC: 7
+ Chaos - Verify bgp admin distance functionality with chaos.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipping test case because of BGP Convergence failure at setup")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure bgp admin distance 200 with CLI in dut.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 200 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Restart frr on R3")
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Verify ebgp and ibgp routes have admin distance of 200 in dut.")
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Restart bgpd process on R3")
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Verify ebgp and ibgp routes have admin distance of 200 in dut.")
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clear BGP")
+ for rtr in topo["routers"]:
+ clear_bgp(tgen, "ipv4", rtr)
+ clear_bgp(tgen, "ipv6", rtr)
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py b/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py
new file mode 100755
index 0000000..559dc93
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_admin_dist_vrf.py
@@ -0,0 +1,900 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 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.
+#
+
+import sys
+import time
+import pytest
+import inspect
+import os
+
+"""Following tests are covered to test bgp admin distance functionality.
+TC_5:
+ Verify bgp admin distance functionality when static route is configured
+ same as bgp learnt route in user vrf.
+
+TC_6: Verify bgp admin distance functionality with ECMP in user vrf.
+
+TC_7:
+ Verify bgp admin distance functionality when routes are
+ imported between VRFs.
+"""
+
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +--------- | R2 |
+ | +-------+
+ |iBGP |
+ +-------+ |
+ | R1 | |iBGP
+ +-------+ |
+ | |
+ | iBGP +-------+ eBGP +-------+
+ +---------- | R3 |----------| R4 |
+ +-------+ +-------+
+ |
+ |eBGP
+ |
+ +-------+
+ | R5 |
+ +-------+
+
+
+"""
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ step,
+ write_test_footer,
+ create_static_routes,
+ verify_rib,
+ check_address_types,
+ reset_config_on_routers,
+ check_router_status,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_best_path_as_per_admin_distance,
+)
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+
+# Global variables
+topo = None
+bgp_convergence = False
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+NETWORK = {
+ "ipv4": [
+ "192.168.20.1/32",
+ "192.168.20.2/32",
+ "192.168.21.1/32",
+ "192.168.21.2/32",
+ "192.168.22.1/32",
+ "192.168.22.2/32",
+ ],
+ "ipv6": [
+ "fc07:50::1/128",
+ "fc07:50::2/128",
+ "fc07:150::1/128",
+ "fc07:150::2/128",
+ "fc07:1::1/128",
+ "fc07:1::2/128",
+ ],
+}
+ADDR_TYPES = check_address_types()
+
+
+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...
+ json_file = "{}/bgp_admin_dist_vrf.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # 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)
+
+ # Checking BGP convergence
+ global bgp_convergence
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """teardown_module.
+
+ Teardown the pytest environment.
+ * `mod`: module name
+ """
+ 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)
+
+
+#####################################################
+# Tests starting
+#####################################################
+
+
+def test_bgp_admin_distance_ebgp_vrf_p0():
+ """
+ TC: 5
+ Verify bgp admin distance functionality when static route is
+ configured same as ebgp learnt route
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipping test case because of BGP Convergence failure at setup")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure bgp admin distance 200 with CLI in dut.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "vrf": "RED",
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 200 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Modify the admin distance value to 150.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 150, "ibgp": 150, "local": 150}
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 150 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 150,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 150,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 150,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 150,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Un configure the admin distance value on DUT")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {
+ "ebgp": 150,
+ "ibgp": 150,
+ "local": 150,
+ "delete": True,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {
+ "ebgp": 150,
+ "ibgp": 150,
+ "local": 150,
+ "delete": True,
+ }
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have default admin distance in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "admin_distance": 20, "vrf": "RED"},
+ {"network": NETWORK["ipv4"][1], "admin_distance": 20, "vrf": "RED"},
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "admin_distance": 20, "vrf": "RED"},
+ {"network": NETWORK["ipv6"][1], "admin_distance": 20, "vrf": "RED"},
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure static route Without any admin distance")
+
+ for addr_type in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects static route.")
+ protocol = "static"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "vrf": "RED"}
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure static route with admin distance of 253")
+ for addr_type in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure admin distance of 254 in bgp for route .")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 254, "ibgp": 254, "local": 254}
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that zebra selects static route.")
+ protocol = "static"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Configure admin distance of 255 in bgp for route in vrf red")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 255, "ibgp": 255, "local": 255}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 255, "ibgp": 255, "local": 255}
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that zebra selects static route.")
+ protocol = "static"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Delete the static route.")
+ for addr_type in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": "Null0",
+ "admin_distance": 253,
+ "delete": True,
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_admin_distance_ebgp_with_imported_rtes_vrf_p0():
+ """
+ TC: 5
+ Verify bgp admin distance functionality when static route is configured
+ same as bgp learnt route in user vrf.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipping test case because of BGP Convergence failure at setup")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure base config as per the topology")
+ reset_config_on_routers(tgen)
+ step("Configure bgp admin distance 200 with CLI in dut.")
+ step(" Import route from vrf to default vrf")
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "vrf": "RED",
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ },
+ },
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
+ "import": {"vrf": "RED"},
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
+ "import": {
+ "vrf": "RED",
+ },
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp routes have admin distance of 200 in dut.")
+ # Verifying best path
+ dut = "r3"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 200,
+ "vrf": "RED",
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], attribute, vrf="RED"
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that routes are getting imported without any issues and "
+ "routes are calculated and installed in rib."
+ )
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ }
+
+ step("Verify that zebra selects bgp route.")
+ protocol = "bgp"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step(" Un configure import route vrf red inside default vrf.")
+ input_dict_1 = {
+ "r3": {
+ "bgp": [
+ {
+ "vrf": "RED",
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200}
+ }
+ },
+ },
+ },
+ {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
+ "import": {"vrf": "RED", "delete": True},
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "distance": {"ebgp": 200, "ibgp": 200, "local": 200},
+ "import": {"vrf": "RED", "delete": True},
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "ipv4": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv4"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "admin_distance": 200,
+ },
+ {
+ "network": NETWORK["ipv6"][1],
+ "admin_distance": 200,
+ },
+ ]
+ }
+ },
+ }
+
+ step("Verify that route withdrawal happens properly.")
+ protocol = "bgp"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result4 is not True
+ ), "Testcase {} : Failed \n Route is not withdrawn. Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_distance_change/test_bgp_distance_change.py b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
new file mode 100644
index 0000000..a7040db
--- /dev/null
+++ b/tests/topotests/bgp_distance_change/test_bgp_distance_change.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+
+#
+# bgp_distance_change.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+bgp_distance_change.py:
+
+Test if works the following commands:
+router bgp 65031
+ address-family ipv4 unicast
+ distance bgp 123 123 123
+
+Changed distance should reflect to RIB after changes.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_invalid():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_distance_change(router):
+ router.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65000
+ address-family ipv4 unicast
+ distance bgp 123 123 123
+ """
+ )
+
+ def _bgp_check_distance_change(router):
+ output = json.loads(router.vtysh_cmd("show ip route 172.16.255.254/32 json"))
+ expected = {"172.16.255.254/32": [{"protocol": "bgp", "distance": 123}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+
+ assert result is None, 'Failed to see BGP convergence in "{}"'.format(router)
+
+ _bgp_distance_change(router)
+
+ test_func = functools.partial(_bgp_check_distance_change, router)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+
+ assert result is None, 'Failed to see applied BGP distance in RIB "{}"'.format(
+ router
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_dont_capability_negotiate/__init__.py b/tests/topotests/bgp_dont_capability_negotiate/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/__init__.py
diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf
new file mode 100644
index 0000000..b429efe
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 dont-capability-negotiate
+!
diff --git a/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf b/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf b/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf
new file mode 100644
index 0000000..4af2cd6
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r2/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf b/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf
new file mode 100644
index 0000000..dc15cf7
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py
new file mode 100644
index 0000000..272fdd3
--- /dev/null
+++ b/tests/topotests/bgp_dont_capability_negotiate/test_bgp_dont_capability_negotiate.py
@@ -0,0 +1,96 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if BGP connection is established if at least one peer
+sets `dont-capability-negotiate`.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_dont_capability_negotiate():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast summary json"))
+ expected = {
+ "peers": {
+ "192.168.1.2": {
+ "pfxRcd": 2,
+ "pfxSnt": 2,
+ "state": "Established",
+ "peerState": "OK",
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't converge with dont-capability-negotiate"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/__init__.py
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf
new file mode 100644
index 0000000..c320bb5
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.101 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf
new file mode 100644
index 0000000..1782edc
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.1.1/32
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf
new file mode 100644
index 0000000..cb712e9
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/bgpd.conf
@@ -0,0 +1,3 @@
+router bgp 65103
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.101 remote-as external
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf
new file mode 100644
index 0000000..968171e
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.103/24
+!
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf
new file mode 100644
index 0000000..a6e3260
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65000
+ bgp router-id 192.168.1.101
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.103 remote-as external
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf
new file mode 100644
index 0000000..ddcf862
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/r3/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r3-eth0
+ ip address 192.168.1.101/24
+!
diff --git a/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
new file mode 100644
index 0000000..0fc9d9d
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_common_subnet_nexthop_unchanged/test_bgp-ebgp-common-subnet-nexthop-unchanged.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+https://tools.ietf.org/html/rfc4271
+
+Check if NEXT_HOP attribute is not changed if peer X shares a
+common subnet with this address.
+
+- Otherwise, if the route being announced was learned from an
+ external peer, the speaker can use an IP address of any
+ adjacent router (known from the received NEXT_HOP attribute)
+ that the speaker itself uses for local route calculation in
+ the NEXT_HOP attribute, provided that peer X shares a common
+ subnet with this address. This is a second form of "third
+ party" NEXT_HOP attribute.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_ebgp_common_subnet_nh_unchanged():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {
+ "192.168.1.1": {"state": "Established"},
+ "192.168.1.103": {"state": "Established"},
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, r3)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(r3)
+
+ def _bgp_nh_unchanged(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json"))
+ expected = {"paths": [{"nexthops": [{"ip": "192.168.1.1"}]}]}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_nh_unchanged, r2)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Wrong next-hop in "{}"'.format(r2)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ebgp_requires_policy/__init__.py b/tests/topotests/bgp_ebgp_requires_policy/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/__init__.py
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf
new file mode 100644
index 0000000..add37ee
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.255.2 route-map outgoing out
+!
+ip prefix-list peer-out permit 172.16.255.254/32
+route-map outgoing permit 10
+ match ip address prefix-list peer-out
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf
new file mode 100644
index 0000000..802c327
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 1000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf
new file mode 100644
index 0000000..1280b42
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r3/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ redistribute connected
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf
new file mode 100644
index 0000000..39499a1
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r3-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf
new file mode 100644
index 0000000..802c327
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r4/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 1000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf
new file mode 100644
index 0000000..b859115
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf
new file mode 100644
index 0000000..60b29f6
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r5/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65000
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf
new file mode 100644
index 0000000..7ef77a6
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r5/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r5-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf
new file mode 100644
index 0000000..7ad5d92
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r6/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 65000
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf
new file mode 100644
index 0000000..1c617c4
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/r6/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r6-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
new file mode 100644
index 0000000..e6fe22b
--- /dev/null
+++ b/tests/topotests/bgp_ebgp_requires_policy/test_bgp_ebgp_requires_policy.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+
+#
+# bgp_ebgp_requires_policy.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+bgp_ebgp_requires_policy.py:
+
+Test if eBGP sender without a filter applied to the peer is allowed
+to send advertisements.
+
+Scenario 1:
+ r1 has a filter applied for outgoing direction,
+ r2 receives 192.168.255.1/32.
+
+Scenario 2:
+ r3 hasn't a filter appied for outgoing direction,
+ r4 does not receive 192.168.255.1/32.
+
+Scenario 3:
+ r5 and r6 establish iBGP session which in turn should ignore
+ RFC8212. All routes for both directions MUST work.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 7):
+ tgen.add_router("r{}".format(routern))
+
+ # Scenario 1.
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Scenario 2.
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # Scenario 3.
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r6"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ebgp_requires_policy():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
+ expected = {"192.168.255.1": {"bgpState": "Established"}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_routes(router):
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd(
+ "show ip bgp neighbor 192.168.255.1 routes json"
+ )
+ )
+ expected = {"routes": {"172.16.255.254/32": [{"valid": True}]}}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_advertised_routes(router):
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd(
+ "show ip bgp neighbor 192.168.255.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {},
+ "totalPrefixCounter": 0,
+ "filteredPrefixCounter": 0,
+ }
+ return topotest.json_cmp(output, expected)
+
+ # Scenario 1.
+ logger.info("Scenario 1: r2 receives 192.168.255.1/32 from r1")
+ test_func = functools.partial(_bgp_converge, "r2")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "Failed bgp convergence (r2)"
+
+ test_func = functools.partial(_bgp_has_routes, "r2")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "r2 does not receive 192.168.255.1/32"
+
+ # Scenario 2.
+ logger.info("Scenario 2: r3 must not send 192.168.255.1/32 to r4")
+ test_func = functools.partial(_bgp_converge, "r4")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "Failed bgp convergence (r4)"
+
+ test_func = functools.partial(_bgp_advertised_routes, "r3")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "r3 announced 192.168.255.1/32 to r4"
+
+ # Scenario 3.
+ logger.info("Scenario 3: r6 receives 192.168.255.1/32 from r5 (iBGP)")
+ test_func = functools.partial(_bgp_converge, "r6")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "Failed bgp convergence (r6)"
+
+ test_func = functools.partial(_bgp_has_routes, "r6")
+ success, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert success is True, "r6 does not receive 192.168.255.1/32"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ecmp_topo1/__init__.py b/tests/topotests/bgp_ecmp_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/__init__.py
diff --git a/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot
new file mode 100644
index 0000000..90295e1
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.dot
@@ -0,0 +1,206 @@
+## 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 ospf_ecmp_iBGP_topo1 {
+ label="bgp ecmp topo1 - eBGP with different AS numbers";
+ labelloc="t";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.255.1/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # 4 Switches for eBGP Peers
+ s1 [
+ label="s1\n10.0.1.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.2.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ label="s3\n10.0.3.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ label="s4\n10.0.4.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # 20 ExaBGP Peers AS 101...120
+ peer1 [
+ label="eBGP peer1\nAS99\nrtr-id 10.0.1.101/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer2 [
+ label="eBGP peer2\nAS99\nrtr-id 10.0.1.102/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer3 [
+ label="eBGP peer3\nAS99\nrtr-id 10.0.1.103/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer4 [
+ label="eBGP peer4\nAS99\nrtr-id 10.0.1.104/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer5 [
+ label="eBGP peer5\nAS99\nrtr-id 10.0.1.105/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer6 [
+ label="eBGP peer6\nAS99\nrtr-id 10.0.2.106/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer7 [
+ label="eBGP peer7\nAS99\nrtr-id 10.0.2.107/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer8 [
+ label="eBGP peer8\nAS99\nrtr-id 10.0.2.108/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer9 [
+ label="eBGP peer9\nAS99\nrtr-id 10.0.2.109/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer10 [
+ label="eBGP peer10\nAS99\nrtr-id 10.0.2.110/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer11 [
+ label="eBGP peer11\nAS111\nrtr-id 10.0.3.111/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer12 [
+ label="eBGP peer12\nAS112\nrtr-id 10.0.3.112/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer13 [
+ label="eBGP peer13\nAS113\nrtr-id 10.0.3.113/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer14 [
+ label="eBGP peer14\nAS114\nrtr-id 10.0.3.114/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer15 [
+ label="eBGP peer15\nAS115\nrtr-id 10.0.3.115/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer16 [
+ label="eBGP peer16\nAS116\nrtr-id 10.0.4.116/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer17 [
+ label="eBGP peer17\nAS117\nrtr-id 10.0.4.117/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer18 [
+ label="eBGP peer18\nAS118\nrtr-id 10.0.4.118/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer19 [
+ label="eBGP peer19\nAS119\nrtr-id 10.0.4.119/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+ peer20 [
+ label="eBGP peer20\nAS120\nrtr-id 10.0.4.120/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="eth0\n.1"];
+ r1 -- s2 [label="eth1\n.1"];
+ r1 -- s3 [label="eth2\n.1"];
+ r1 -- s4 [label="eth3\n.1"];
+
+ peer1 -- s1 [label="eth0\n.101"];
+ peer2 -- s1 [label="eth0\n.102"];
+ peer3 -- s1 [label="eth0\n.103"];
+ peer4 -- s1 [label="eth0\n.104"];
+ peer5 -- s1 [label="eth0\n.105"];
+ peer6 -- s2 [label="eth0\n.106"];
+ peer7 -- s2 [label="eth0\n.107"];
+ peer8 -- s2 [label="eth0\n.108"];
+ peer9 -- s2 [label="eth0\n.109"];
+ peer10 -- s2 [label="eth0\n.110"];
+ peer11 -- s3 [label="eth0\n.111"];
+ peer12 -- s3 [label="eth0\n.112"];
+ peer13 -- s3 [label="eth0\n.113"];
+ peer14 -- s3 [label="eth0\n.114"];
+ peer15 -- s3 [label="eth0\n.115"];
+ peer16 -- s4 [label="eth0\n.116"];
+ peer17 -- s4 [label="eth0\n.117"];
+ peer18 -- s4 [label="eth0\n.118"];
+ peer19 -- s4 [label="eth0\n.119"];
+ peer20 -- s4 [label="eth0\n.120"];
+
+ # Arrange network to make cleaner diagram
+ { rank=same peer1 peer2 peer3 peer4 peer5 } -- s1 -- { rank=same peer6 peer7 peer8 peer9 peer10 } -- s2
+ -- { rank=same peer11 peer12 peer13 peer14 peer15 } -- s3 -- { rank=same peer16 peer17 peer18 peer19 peer20 } -- s4
+ -- { rank=same r1 } [style=invis]
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf
new file mode 100644
index 0000000..b4d4f6a
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/bgp-ecmp-topo1.pdf
Binary files differ
diff --git a/tests/topotests/bgp_ecmp_topo1/exabgp.env b/tests/topotests/bgp_ecmp_topo1/exabgp.env
new file mode 100644
index 0000000..a328e04
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/exabgp.env
@@ -0,0 +1,54 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer1/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg
new file mode 100644
index 0000000..2d0ca89
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer1/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 1 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 1";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.101;
+ local-address 10.0.1.101;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer10/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg
new file mode 100644
index 0000000..0c842a0
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer10/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 10 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 10";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.110;
+ local-address 10.0.2.110;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer11/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg
new file mode 100644
index 0000000..936dc57
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer11/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 11 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 11";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.111;
+ local-address 10.0.3.111;
+ local-as 111;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer12/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg
new file mode 100644
index 0000000..56b33ea
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer12/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 12 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 12";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.112;
+ local-address 10.0.3.112;
+ local-as 112;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer13/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg
new file mode 100644
index 0000000..b933ffb
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer13/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 13 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 13";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.113;
+ local-address 10.0.3.113;
+ local-as 113;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer14/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg
new file mode 100644
index 0000000..bcfa41e
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer14/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 14 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 14";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.114;
+ local-address 10.0.3.114;
+ local-as 114;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer15/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg
new file mode 100644
index 0000000..022e835
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer15/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 15 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 15";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.115;
+ local-address 10.0.3.115;
+ local-as 115;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer16/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg
new file mode 100644
index 0000000..0649202
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer16/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 16 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 16";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.116;
+ local-address 10.0.4.116;
+ local-as 116;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer17/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg
new file mode 100644
index 0000000..0aeeed9
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer17/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 17 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 17";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.117;
+ local-address 10.0.4.117;
+ local-as 117;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer18/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg
new file mode 100644
index 0000000..352c030
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer18/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 18 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 18";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.118;
+ local-address 10.0.4.118;
+ local-as 118;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer19/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg
new file mode 100644
index 0000000..9913c22
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer19/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 19 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 19";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.119;
+ local-address 10.0.4.119;
+ local-as 119;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer2/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg
new file mode 100644
index 0000000..46b436d
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer2/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 2 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 2";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.102;
+ local-address 10.0.1.102;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer20/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg
new file mode 100644
index 0000000..17fb816
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer20/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 20 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 20";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.120;
+ local-address 10.0.4.120;
+ local-as 120;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer3/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg
new file mode 100644
index 0000000..acd5775
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer3/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 3 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 3";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.103;
+ local-address 10.0.1.103;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer4/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg
new file mode 100644
index 0000000..4c9a989
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer4/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 4 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 4";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.104;
+ local-address 10.0.1.104;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer5/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg
new file mode 100644
index 0000000..eba2aae
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer5/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 5 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 5";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.105;
+ local-address 10.0.1.105;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer6/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg
new file mode 100644
index 0000000..38b6af0
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer6/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 6 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 6";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.106;
+ local-address 10.0.2.106;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer7/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg
new file mode 100644
index 0000000..7631e43
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer7/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 7 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 7";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.107;
+ local-address 10.0.2.107;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer8/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg
new file mode 100644
index 0000000..1cd1cd9
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer8/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 8 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 8";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.108;
+ local-address 10.0.2.108;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py
new file mode 100755
index 0000000..6bef355
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer9/exa-send.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+if peer <= 10:
+ asnum = 99
+else:
+ asnum = peer + 100
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes per PE - different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.202.%s.0/24 med 100 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.203.%s.0/24 med %i next-hop 10.0.%i.%i origin igp\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Announce numRoutes equal routes with different med per PE and different neighbor AS, but same source AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.204.%s.0/24 med %i next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (i, peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+ )
+ stdout.flush()
+
+# Announce 2 different route per peer
+stdout.write(
+ "announce route 10.205.%i.0/24 next-hop 10.0.%i.%i origin igp\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100)
+)
+stdout.write(
+ "announce route 10.206.%i.0/24 next-hop 10.0.%i.%i origin igp as-path [ %i 200 ]\n"
+ % (peer, (((peer - 1) / 5) + 1), peer + 100, asnum)
+)
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg
new file mode 100644
index 0000000..5771553
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/peer9/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 9 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 9";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.109;
+ local-address 10.0.2.109;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf b/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..49981ac
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/r1/bgpd.conf
@@ -0,0 +1,51 @@
+!
+hostname r1
+log file bgpd.log
+!
+router bgp 100
+ bgp router-id 10.0.255.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.101 remote-as 99
+ neighbor 10.0.1.101 timers 3 10
+ neighbor 10.0.1.102 remote-as 99
+ neighbor 10.0.1.102 timers 3 10
+ neighbor 10.0.1.103 remote-as 99
+ neighbor 10.0.1.103 timers 3 10
+ neighbor 10.0.1.104 remote-as 99
+ neighbor 10.0.1.104 timers 3 10
+ neighbor 10.0.1.105 remote-as 99
+ neighbor 10.0.1.105 timers 3 10
+ neighbor 10.0.2.106 remote-as 99
+ neighbor 10.0.2.106 timers 3 10
+ neighbor 10.0.2.107 remote-as 99
+ neighbor 10.0.2.107 timers 3 10
+ neighbor 10.0.2.108 remote-as 99
+ neighbor 10.0.2.108 timers 3 10
+ neighbor 10.0.2.109 remote-as 99
+ neighbor 10.0.2.109 timers 3 10
+ neighbor 10.0.2.110 remote-as 99
+ neighbor 10.0.2.110 timers 3 10
+ neighbor 10.0.3.111 remote-as 111
+ neighbor 10.0.3.111 timers 3 10
+ neighbor 10.0.3.112 remote-as 112
+ neighbor 10.0.3.112 timers 3 10
+ neighbor 10.0.3.113 remote-as 113
+ neighbor 10.0.3.113 timers 3 10
+ neighbor 10.0.3.114 remote-as 114
+ neighbor 10.0.3.114 timers 3 10
+ neighbor 10.0.3.115 remote-as 115
+ neighbor 10.0.3.115 timers 3 10
+ neighbor 10.0.4.116 remote-as 116
+ neighbor 10.0.4.116 timers 3 10
+ neighbor 10.0.4.117 remote-as 117
+ neighbor 10.0.4.117 timers 3 10
+ neighbor 10.0.4.118 remote-as 118
+ neighbor 10.0.4.118 timers 3 10
+ neighbor 10.0.4.119 remote-as 119
+ neighbor 10.0.4.119 timers 3 10
+ neighbor 10.0.4.120 remote-as 120
+ neighbor 10.0.4.120 timers 3 10
+ !
+!
+
diff --git a/tests/topotests/bgp_ecmp_topo1/r1/summary.txt b/tests/topotests/bgp_ecmp_topo1/r1/summary.txt
new file mode 100644
index 0000000..68de28a
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/r1/summary.txt
@@ -0,0 +1,131 @@
+{
+"ipv4Unicast":{
+ "routerId":"10.0.255.1",
+ "as":100,
+ "vrfName":"default",
+ "peerCount":20,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.102":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.103":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.104":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.105":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.106":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.107":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.108":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.109":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.110":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.111":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.112":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.113":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.114":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.115":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.116":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.117":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.118":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.119":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.120":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ }
+ },
+ "totalPeers":20
+}
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt b/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt
new file mode 100644
index 0000000..4895cdb
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/r1/summary20.txt
@@ -0,0 +1,129 @@
+{
+ "routerId":"10.0.255.1",
+ "as":100,
+ "vrfName":"default",
+ "peerCount":20,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.102":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.103":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.104":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.1.105":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.106":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.107":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.108":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.109":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.2.110":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.111":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.112":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.113":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.114":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.3.115":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.116":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.117":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.118":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.119":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ },
+ "10.0.4.120":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":42,
+ "state":"Established"
+ }
+ },
+ "totalPeers":20
+}
diff --git a/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf b/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf
new file mode 100644
index 0000000..77c76cd
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/r1/zebra.conf
@@ -0,0 +1,16 @@
+!
+hostname r1
+log file zebra.log
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.2.1/24
+!
+interface r1-eth2
+ ip address 10.0.3.1/24
+!
+interface r1-eth3
+ ip address 10.0.4.1/24
+!
diff --git a/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py
new file mode 100644
index 0000000..96e4bf6
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo1/test_bgp_ecmp_topo1.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python2
+
+#
+# test_bgp_ecmp_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_bgp_ecmp_topo1.py: Test BGP topology with ECMP (Equal Cost MultiPath).
+"""
+
+import json
+import functools
+import os
+import sys
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+total_ebgp_peers = 20
+
+#####################################################
+#
+# Network Topology Definition
+#
+#####################################################
+
+
+def build_topo(tgen):
+ router = tgen.add_router("r1")
+
+ # Setup Switches - 1 switch per 5 peering routers
+ for swNum in range(1, (total_ebgp_peers + 4) // 5 + 1):
+ switch = tgen.add_switch("s{}".format(swNum))
+ switch.add_link(router)
+
+ # Add 'total_ebgp_peers' number of eBGP ExaBGP neighbors
+ for peerNum in range(1, total_ebgp_peers + 1):
+ swNum = (peerNum - 1) // 5 + 1
+
+ peer_ip = "10.0.{}.{}".format(swNum, peerNum + 100)
+ peer_route = "via 10.0.{}.1".format(swNum)
+ peer = tgen.add_exabgp_peer(
+ "peer{}".format(peerNum), ip=peer_ip, defaultRoute=peer_route
+ )
+
+ switch = tgen.gears["s{}".format(swNum)]
+ switch.add_link(peer)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.start()
+
+ # Starting Hosts and init ExaBGP on each of them
+ topotest.sleep(10, "starting BGP on all {} peers".format(total_ebgp_peers))
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ peer_dir = os.path.join(CWD, pname)
+ env_file = os.path.join(CWD, "exabgp.env")
+ peer.start(peer_dir, env_file)
+ logger.info(pname)
+
+
+def teardown_module(module):
+ del module
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ "Test for BGP topology convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Expected result
+ router = tgen.gears["r1"]
+ if router.has_version("<", "3.0"):
+ reffile = os.path.join(CWD, "r1/summary20.txt")
+ else:
+ reffile = os.path.join(CWD, "r1/summary.txt")
+
+ expected = json.loads(open(reffile).read())
+
+ def _output_summary_cmp(router, cmd, data):
+ """
+ Runs `cmd` that returns JSON data (normally the command ends
+ with 'json') and compare with `data` contents.
+ """
+ output = router.vtysh_cmd(cmd, isjson=True)
+ return topotest.json_cmp(output, data)
+
+ test_func = functools.partial(
+ _output_summary_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assertmsg = "BGP router network did not converge"
+ assert res is None, assertmsg
+
+
+def test_bgp_ecmp():
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ expect = {
+ "routerId": "10.0.255.1",
+ "routes": {},
+ }
+
+ for net in range(1, 5):
+ for subnet in range(0, 10):
+ netkey = "10.20{}.{}.0/24".format(net, subnet)
+ expect["routes"][netkey] = []
+ for _ in range(0, 10):
+ peer = {"multipath": True, "valid": True}
+ expect["routes"][netkey].append(peer)
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, tgen.gears["r1"], "show ip bgp json", expect
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = 'expected multipath routes in "show ip bgp" output'
+ assert res is None, assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json b/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json
new file mode 100755
index 0000000..34f11c0
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/ebgp_ecmp_topo2.json
@@ -0,0 +1,834 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link5": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link6": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link7": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link8": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link9": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link10": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link11": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link12": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link13": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link14": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link15": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link16": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link17": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link18": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link19": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link20": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link21": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link22": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link23": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link24": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link25": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link26": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link27": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link28": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link29": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link30": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link31": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link32": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json b/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json
new file mode 100755
index 0000000..9eea907
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/ibgp_ecmp_topo2.json
@@ -0,0 +1,844 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "next_hop_self": true
+ },
+ "r2-link3": {
+ "next_hop_self": true
+ },
+ "r2-link4": {
+ "next_hop_self": true
+ },
+ "r2-link5": {
+ "next_hop_self": true
+ },
+ "r2-link6": {
+ "next_hop_self": true
+ },
+ "r2-link7": {
+ "next_hop_self": true
+ },
+ "r2-link8": {
+ "next_hop_self": true
+ },
+ "r2-link9": {
+ "next_hop_self": true
+ },
+ "r2-link10": {
+ "next_hop_self": true
+ },
+ "r2-link11": {
+ "next_hop_self": true
+ },
+ "r2-link12": {
+ "next_hop_self": true
+ },
+ "r2-link13": {
+ "next_hop_self": true
+ },
+ "r2-link14": {
+ "next_hop_self": true
+ },
+ "r2-link15": {
+ "next_hop_self": true
+ },
+ "r2-link16": {
+ "next_hop_self": true
+ },
+ "r2-link17": {
+ "next_hop_self": true
+ },
+ "r2-link18": {
+ "next_hop_self": true
+ },
+ "r2-link19": {
+ "next_hop_self": true
+ },
+ "r2-link20": {
+ "next_hop_self": true
+ },
+ "r2-link21": {
+ "next_hop_self": true
+ },
+ "r2-link22": {
+ "next_hop_self": true
+ },
+ "r2-link23": {
+ "next_hop_self": true
+ },
+ "r2-link24": {
+ "next_hop_self": true
+ },
+ "r2-link25": {
+ "next_hop_self": true
+ },
+ "r2-link26": {
+ "next_hop_self": true
+ },
+ "r2-link27": {
+ "next_hop_self": true
+ },
+ "r2-link28": {
+ "next_hop_self": true
+ },
+ "r2-link29": {
+ "next_hop_self": true
+ },
+ "r2-link30": {
+ "next_hop_self": true
+ },
+ "r2-link31": {
+ "next_hop_self": true
+ },
+ "r2-link32": {
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link8": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link9": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link10": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link11": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link12": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link13": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link14": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link15": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link16": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link17": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link18": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link19": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link20": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link21": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link22": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link23": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link24": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link25": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link26": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link27": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link28": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link29": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link30": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link31": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link32": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {},
+ "r3-link5": {},
+ "r3-link6": {},
+ "r3-link7": {},
+ "r3-link8": {},
+ "r3-link9": {},
+ "r3-link10": {},
+ "r3-link11": {},
+ "r3-link12": {},
+ "r3-link13": {},
+ "r3-link14": {},
+ "r3-link15": {},
+ "r3-link16": {},
+ "r3-link17": {},
+ "r3-link18": {},
+ "r3-link19": {},
+ "r3-link20": {},
+ "r3-link21": {},
+ "r3-link22": {},
+ "r3-link23": {},
+ "r3-link24": {},
+ "r3-link25": {},
+ "r3-link26": {},
+ "r3-link27": {},
+ "r3-link28": {},
+ "r3-link29": {},
+ "r3-link30": {},
+ "r3-link31": {},
+ "r3-link32": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 32
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link5": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link6": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link7": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link8": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link9": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link10": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link11": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link12": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link13": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link14": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link15": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link16": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link17": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link18": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link19": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link20": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link21": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link22": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link23": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link24": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link25": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link26": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link27": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link28": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link29": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link30": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link31": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link32": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py
new file mode 100644
index 0000000..2784e95
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/test_ebgp_ecmp_topo2.py
@@ -0,0 +1,753 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 ecmp functionality on EBGP.
+1. Verify routes installed as per maximum-paths configuration (8/16/32)
+2. Disable/Shut selected paths nexthops and verify other next are installed in
+ the RIB of DUT. Enable interfaces and verify RIB count.
+3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors.
+4. Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed.
+5. Shut BGP neighbors one by one and verify BGP and routing table updated
+ accordingly in DUT
+6. Delete static routes and verify routers are cleared from BGP table and RIB
+ of DUT.
+7. Verify routes are cleared from BGP and RIB table of DUT when advertise
+ network configuration is removed.
+"""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ interface_status,
+ reset_config_on_routers,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NEXT_HOPS = {"ipv4": [], "ipv6": []}
+INTF_LIST_R3 = []
+INTF_LIST_R2 = []
+NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"}
+NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
+BGP_CONVERGENCE = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment.
+
+ * `mod`: module name
+ """
+ global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/ebgp_ecmp_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # tgen.mininet_cli()
+ # Api call verify whether BGP is converged
+ 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
+ )
+
+ link_data = [
+ val for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links
+ ]
+ for adt in ADDR_TYPES:
+ NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data]
+ if adt == "ipv4":
+ NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2]))
+ elif adt == "ipv6":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16)
+ )
+
+ INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1]))
+
+ link_data = [
+ val for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links
+ ]
+ INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1]))
+
+ # STATIC_ROUTE = True
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def static_or_nw(tgen, topo, tc_name, test_type, dut):
+
+ if test_type == "redist_static":
+ input_dict_static = {
+ dut: {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]},
+ ]
+ }
+ }
+ logger.info("Configuring static route on router %s", dut)
+ result = create_static_routes(tgen, input_dict_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static route on router %s", dut)
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ elif test_type == "advertise_nw":
+ input_dict_nw = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv4"]}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv6"]}]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info(
+ "Advertising networks %s %s from router %s",
+ NETWORK["ipv4"],
+ NETWORK["ipv6"],
+ dut,
+ )
+ result = create_router_bgp(tgen, topo, input_dict_nw)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
+ """
+ Verify routes installed as per maximum-paths
+ configuration (8/16/32).
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": ecmp_num,
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num)
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+
+ # Only test the count of nexthops; the actual nexthop addresses
+ # can vary and are not deterministic.
+ #
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ count_only=True,
+ )
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_ecmp_after_clear_bgp(request, ecmp_num, test_type):
+ """Verify BGP table and RIB in DUT after clear BGP routes and neighbors"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Clear BGP
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, dut)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_redistribute_static(request):
+ """Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3 are deleted", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ logger.info("Enable redistribute static")
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "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)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_ecmp_shut_bgp_neighbor(request, test_type):
+ """Shut BGP neighbors one by one and verify BGP and routing table updated
+ accordingly in DUT"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ logger.info(INTF_LIST_R2)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for intf_num in range(len(INTF_LIST_R2) + 1, 16):
+ intf_val = INTF_LIST_R2[intf_num : intf_num + 16]
+
+ input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}}
+ logger.info("Shutting down neighbor interface {} on r2".format(intf_val))
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ if intf_num + 16 < 32:
+ check_hops = NEXT_HOPS[addr_type]
+ else:
+ check_hops = []
+
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}}
+
+ logger.info("Enabling all neighbor interface {} on r2")
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_static_route(request):
+ """
+ Delete static routes and verify routers are cleared from BGP table,
+ and RIB of DUT.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Remove static routes")
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Verifying %s routes on r3 are removed", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_2,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Enable static route")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+def test_ecmp_remove_nw_advertise(request):
+ """
+ Verify routes are cleared from BGP and RIB table of DUT,
+ when advertise network configuration is removed
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv4"], "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv6"], "delete": True}
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Withdraw advertised networks")
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ 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/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py
new file mode 100644
index 0000000..704e8fd
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo2/test_ibgp_ecmp_topo2.py
@@ -0,0 +1,754 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 ecmp functionality on EBGP.
+1. Verify routes installed as per maximum-paths configuration (8/16/32)
+2. Disable/Shut selected paths nexthops and verify other next are installed in
+ the RIB of DUT. Enable interfaces and verify RIB count.
+3. Verify BGP table and RIB in DUT after clear BGP routes and neighbors.
+4. Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed.
+5. Shut BGP neighbors one by one and verify BGP and routing table updated
+ accordingly in DUT
+6. Delete static routes and verify routers are cleared from BGP table and RIB
+ of DUT.
+7. Verify routes are cleared from BGP and RIB table of DUT when advertise
+ network configuration is removed.
+"""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ interface_status,
+ reset_config_on_routers,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NEXT_HOPS = {"ipv4": [], "ipv6": []}
+INTF_LIST_R3 = []
+INTF_LIST_R2 = []
+NETWORK = {"ipv4": "11.0.20.1/32", "ipv6": "1::/64"}
+NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
+BGP_CONVERGENCE = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment.
+
+ * `mod`: module name
+ """
+ global NEXT_HOPS, INTF_LIST_R3, INTF_LIST_R2, TEST_STATIC
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/ibgp_ecmp_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # tgen.mininet_cli()
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ link_data = [
+ val for links, val in topo["routers"]["r2"]["links"].items() if "r3" in links
+ ]
+ for adt in ADDR_TYPES:
+ NEXT_HOPS[adt] = [val[adt].split("/")[0] for val in link_data]
+ if adt == "ipv4":
+ NEXT_HOPS[adt] = sorted(NEXT_HOPS[adt], key=lambda x: int(x.split(".")[2]))
+ elif adt == "ipv6":
+ NEXT_HOPS[adt] = sorted(
+ NEXT_HOPS[adt], key=lambda x: int(x.split(":")[-3], 16)
+ )
+
+ INTF_LIST_R2 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R2 = sorted(INTF_LIST_R2, key=lambda x: int(x.split("eth")[1]))
+
+ link_data = [
+ val for links, val in topo["routers"]["r3"]["links"].items() if "r2" in links
+ ]
+ INTF_LIST_R3 = [val["interface"].split("/")[0] for val in link_data]
+ INTF_LIST_R3 = sorted(INTF_LIST_R3, key=lambda x: int(x.split("eth")[1]))
+
+ # STATIC_ROUTE = True
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def static_or_nw(tgen, topo, tc_name, test_type, dut):
+
+ if test_type == "redist_static":
+ input_dict_static = {
+ dut: {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]},
+ ]
+ }
+ }
+ logger.info("Configuring static route on router %s", dut)
+ result = create_static_routes(tgen, input_dict_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static route on router %s", dut)
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ elif test_type == "advertise_nw":
+ input_dict_nw = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv4"]}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv6"]}]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info(
+ "Advertising networks %s %s from router %s",
+ NETWORK["ipv4"],
+ NETWORK["ipv6"],
+ dut,
+ )
+ result = create_router_bgp(tgen, topo, input_dict_nw)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_modify_ecmp_max_paths(request, ecmp_num, test_type):
+ """
+ Verify routes installed as per maximum-paths
+ configuration (8/16/32).
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": ecmp_num,
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring bgp maximum-paths %s on router r3", ecmp_num)
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+
+ # Test only the count of nexthops, not the specific nexthop addresses -
+ # they're not deterministic
+ #
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ count_only=True,
+ )
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("ecmp_num", ["8", "16", "32"])
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_ecmp_after_clear_bgp(request, ecmp_num, test_type):
+ """Verify BGP table and RIB in DUT after clear BGP routes and neighbors"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Clear BGP
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, dut)
+
+ # Verify BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type][: int(ecmp_num)],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_redistribute_static(request):
+ """Verify routes are cleared from BGP and RIB table of DUT when
+ redistribute static configuration is removed."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3 are deleted", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ logger.info("Enable redistribute static")
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "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)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("test_type", ["redist_static", "advertise_nw"])
+def test_ecmp_shut_bgp_neighbor(request, test_type):
+ """Shut BGP neighbors one by one and verify BGP and routing table updated
+ accordingly in DUT"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ logger.info(INTF_LIST_R2)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for intf_num in range(len(INTF_LIST_R2) + 1, 16):
+ intf_val = INTF_LIST_R2[intf_num : intf_num + 16]
+
+ input_dict_1 = {"r2": {"interface_list": [intf_val], "status": "down"}}
+ logger.info("Shutting down neighbor interface {} on r2".format(intf_val))
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ if intf_num + 16 < 32:
+ check_hops = NEXT_HOPS[addr_type]
+ else:
+ check_hops = []
+
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, next_hop=check_hops, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_1 = {"r2": {"interface_list": INTF_LIST_R2, "status": "up"}}
+
+ logger.info("Enabling all neighbor interface {} on r2")
+ result = interface_status(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_static_route(request):
+ """
+ Delete static routes and verify routers are cleared from BGP table,
+ and RIB of DUT.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+
+ static_or_nw(tgen, topo, tc_name, "redist_static", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_1,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Remove static routes")
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Verifying %s routes on r3 are removed", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_2,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": NEXT_HOP_IP[addr_type]}
+ ]
+ }
+ }
+
+ logger.info("Enable static route")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ecmp_remove_nw_advertise(request):
+ """
+ Verify routes are cleared from BGP and RIB table of DUT,
+ when advertise network configuration is removed
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv4"], "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": NETWORK["ipv6"], "delete": True}
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Withdraw advertised networks")
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=[],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes still" " present in RIB".format(tc_name)
+
+ static_or_nw(tgen, topo, tc_name, "advertise_nw", "r2")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=NEXT_HOPS[addr_type],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json
new file mode 100644
index 0000000..b01f902
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo3/ibgp_ecmp_topo3.json
@@ -0,0 +1,232 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "next_hop_self": true
+ },
+ "r2-link2": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "next_hop_self": true
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 2
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180
+ },
+ "r3-link2": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ibgp": 2
+ },
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ },
+ "r3-link2": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py
new file mode 100644
index 0000000..2a51dc8
--- /dev/null
+++ b/tests/topotests/bgp_ecmp_topo3/test_ibgp_ecmp_topo3.py
@@ -0,0 +1,270 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 ecmp functionality on iBGP.
+1. Verify bgp fast-convergence functionality
+"""
+import os
+import sys
+import time
+import pytest
+import re
+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, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import get_topogen
+from lib import topojson
+
+from lib.common_config import (
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ apply_raw_config,
+)
+from lib.topolog import logger
+from lib.bgp import create_router_bgp, verify_bgp_convergence
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NEXT_HOPS = {"ipv4": [], "ipv6": []}
+NETWORK = {"ipv4": "192.168.1.10/32", "ipv6": "fd00:0:0:1::10/128"}
+NEXT_HOP_IP = {"ipv4": "10.0.0.1", "ipv6": "fd00::1"}
+BGP_CONVERGENCE = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment.
+
+ * `mod`: module name
+ """
+ global ADDR_TYPES
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+
+ tgen = topojson.setup_module_from_json(mod.__file__)
+ topo = tgen.json_topo
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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
+ )
+
+ # STATIC_ROUTE = True
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ get_topogen().stop_topology()
+
+
+def static_or_nw(tgen, topo, tc_name, test_type, dut):
+
+ if test_type == "redist_static":
+ input_dict_static = {
+ dut: {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": NEXT_HOP_IP["ipv4"]},
+ {"network": NETWORK["ipv6"], "next_hop": NEXT_HOP_IP["ipv6"]},
+ ]
+ }
+ }
+ logger.info("Configuring static route on router %s", dut)
+ result = create_static_routes(tgen, input_dict_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static route on router %s", dut)
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ elif test_type == "advertise_nw":
+ input_dict_nw = {
+ dut: {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv4"]}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{"network": NETWORK["ipv6"]}]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ logger.info(
+ "Advertising networks %s %s from router %s",
+ NETWORK["ipv4"],
+ NETWORK["ipv6"],
+ dut,
+ )
+ result = create_router_bgp(tgen, topo, input_dict_nw)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+@pytest.mark.parametrize("test_type", ["redist_static"])
+def test_ecmp_fast_convergence(request, test_type, tgen, topo):
+ """This test is to verify bgp fast-convergence cli functionality"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ reset_config_on_routers(tgen)
+ static_or_nw(tgen, topo, tc_name, test_type, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+
+ logger.info("Verifying %s routes on r3", addr_type)
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ intf1 = topo["routers"]["r2"]["links"]["r3-link1"]["interface"]
+ intf2 = topo["routers"]["r2"]["links"]["r3-link2"]["interface"]
+
+ logger.info("Shutdown one of the link b/w r2 and r3")
+ shutdown_bringup_interface(tgen, "r2", intf1, False)
+
+ logger.info("Verify bgp neighbors are still up")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ logger.info("Shutdown another link b/w r2 and r3")
+ shutdown_bringup_interface(tgen, "r2", intf2, False)
+
+ logger.info("Wait for 10 sec and make sure bgp neighbors are still up")
+ sleep(10)
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ logger.info("No shut links b/w r2 and r3")
+ shutdown_bringup_interface(tgen, "r2", intf1, True)
+ shutdown_bringup_interface(tgen, "r2", intf2, True)
+
+ logger.info("Ensure that the links are still up")
+ result = verify_bgp_convergence(tgen, topo)
+
+ logger.info("Enable bgp fast-convergence cli")
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r2"]["bgp"]["local_as"]),
+ "bgp fast-convergence",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ logger.info("Ensure BGP has processed the cli")
+ r2 = tgen.gears["r2"]
+ output = r2.vtysh_cmd("show run")
+ verify = re.search(r"fast-convergence", output )
+ assert verify is not None, (
+ "r2 does not have the fast convergence command yet")
+
+ logger.info("Shutdown one link b/w r2 and r3")
+ shutdown_bringup_interface(tgen, "r2", intf1, False)
+
+ logger.info("Verify bgp neighbors goes down immediately")
+ result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Shutdown second link b/w r2 and r3")
+ shutdown_bringup_interface(tgen, "r2", intf2, False)
+
+ logger.info("Verify bgp neighbors goes down immediately")
+ result = verify_bgp_convergence(tgen, topo, dut="r2", expected=False)
+ assert result is not 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/bgp_evpn_mh/evpn-mh-topo-tests.pdf b/tests/topotests/bgp_evpn_mh/evpn-mh-topo-tests.pdf
new file mode 100644
index 0000000..8858e21
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..e69de29
--- /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 0000000..2e26f60
--- /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 0000000..68e686e
--- /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 0000000..80e9e5a
--- /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 0000000..ec2e789
--- /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 0000000..c156624
--- /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 0000000..1cd1df8
--- /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 100644
index 0000000..b0e4381
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/test_evpn_mh.py
@@ -0,0 +1,809 @@
+#!/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 sys
+import subprocess
+from functools import partial
+
+import pytest
+import json
+import platform
+from functools import partial
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
+
+# 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
+
+# Required to instantiate the topology builder class.
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
+
+#####################################################
+##
+## Network Topology Definition
+##
+## See topology picture at evpn-mh-topo-tests.pdf
+#####################################################
+
+
+def build_topo(tgen):
+ """
+ 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
+ """
+
+ 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(build_topo, 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.items():
+ 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.items():
+ 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.items():
+ 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.items()])
+ 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.get("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.get("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 ping_anycast_gw(tgen):
+ # ping the anycast gw from the local and remote hosts to populate
+ # the mac address on the PEs
+ python3_path = tgen.net.get_exec_path(["python3", "python"])
+ script_path = os.path.abspath(os.path.join(CWD, "../lib/scapy_sendpkt.py"))
+ intf = "torbond"
+ ipaddr = "45.0.0.1"
+ ping_cmd = [
+ python3_path,
+ script_path,
+ "--imports=Ether,ARP",
+ "--interface=" + intf,
+ 'Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst="{}")'.format(ipaddr),
+ ]
+ for name in ("hostd11", "hostd21"):
+ host = tgen.net.hosts[name]
+ _, stdout, _ = host.cmd_status(ping_cmd, warn=False, stderr=subprocess.STDOUT)
+ stdout = stdout.strip()
+ if stdout:
+ host.logger.debug(
+ "%s: arping on %s for %s returned: %s", name, intf, ipaddr, stdout
+ )
+
+
+def check_mac(dut, vni, mac, m_type, esi, intf, ping_gw=False, tgen=None):
+ """
+ checks if mac is present and if desination matches the one provided
+ """
+
+ if ping_gw:
+ ping_anycast_gw(tgen)
+
+ 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.items():
+ 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"])
+
+ 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, True, tgen)
+ _, 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
+
+
+def check_df_role(dut, esi, role):
+ """
+ Return error string if the df role on the dut is different
+ """
+ es_json = dut.vtysh_cmd("show evpn es %s json" % esi)
+ es = json.loads(es_json)
+
+ if not es:
+ return "esi %s not found" % esi
+
+ flags = es.get("flags", [])
+ curr_role = "nonDF" if "nonDF" in flags else "DF"
+
+ if curr_role != role:
+ return "%s is %s for %s" % (dut.name, curr_role, esi)
+
+ return None
+
+
+def test_evpn_df():
+ """
+ 1. Check the DF role on all the PEs on rack-1.
+ 2. Increase the DF preference on the non-DF and check if it becomes
+ the DF winner.
+ """
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # We will run the tests on just one ES
+ esi = host_es_map.get("hostd11")
+ intf = "hostbond1"
+
+ tors = []
+ tors.append(tgen.gears["torm11"])
+ tors.append(tgen.gears["torm12"])
+ df_node = "torm11"
+
+ # check roles on rack-1
+ for tor in tors:
+ role = "DF" if tor.name == df_node else "nonDF"
+ test_fn = partial(check_df_role, tor, esi, role)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" DF role incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+ # change df preference on the nonDF to make it the df
+ torm12 = tgen.gears["torm12"]
+ torm12.vtysh_cmd("conf\ninterface %s\nevpn mh es-df-pref %d" % (intf, 60000))
+ df_node = "torm12"
+
+ # re-check roles on rack-1; we should have a new winner
+ for tor in tors:
+ role = "DF" if tor.name == df_node else "nonDF"
+ test_fn = partial(check_df_role, tor, esi, role)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" DF role incorrect'.format(tor.name)
+ assert result is None, assertmsg
+
+ # tgen.mininet_cli()
+
+
+def check_protodown_rc(dut, protodown_rc):
+ """
+ check if specified protodown reason code is set
+ """
+
+ out = dut.vtysh_cmd("show evpn json")
+
+ evpn_js = json.loads(out)
+ tmp_rc = evpn_js.get("protodownReasons", [])
+
+ if protodown_rc:
+ if protodown_rc not in tmp_rc:
+ return "protodown %s missing in %s" % (protodown_rc, tmp_rc)
+ else:
+ if tmp_rc:
+ return "unexpected protodown rc %s" % (tmp_rc)
+
+ return None
+
+
+def test_evpn_uplink_tracking():
+ """
+ 1. Wait for access ports to come out of startup-delay
+ 2. disable uplinks and check if access ports have been protodowned
+ 3. enable uplinks and check if access ports have been moved out
+ of protodown
+ """
+
+ tgen = get_topogen()
+
+ dut_name = "torm11"
+ dut = tgen.gears[dut_name]
+
+ # wait for protodown rc to clear after startup
+ test_fn = partial(check_protodown_rc, dut, None)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" protodown rc incorrect'.format(dut_name)
+ assert result is None, assertmsg
+
+ # disable the uplinks
+ dut.run("ip link set %s-eth0 down" % dut_name)
+ dut.run("ip link set %s-eth1 down" % dut_name)
+
+ # check if the access ports have been protodowned
+ test_fn = partial(check_protodown_rc, dut, "uplinkDown")
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" protodown rc incorrect'.format(dut_name)
+ assert result is None, assertmsg
+
+ # enable the uplinks
+ dut.run("ip link set %s-eth0 up" % dut_name)
+ dut.run("ip link set %s-eth1 up" % dut_name)
+
+ # check if the access ports have been moved out of protodown
+ test_fn = partial(check_protodown_rc, dut, None)
+ _, result = topotest.run_and_expect(test_fn, None, count=20, wait=3)
+ assertmsg = '"{}" protodown rc incorrect'.format(dut_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 0000000..2c1c695
--- /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 0000000..fbba735
--- /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 0000000..a88370d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm11/zebra.conf
@@ -0,0 +1,27 @@
+! debug zebra evpn mh es
+! debug zebra evpn mh mac
+! debug zebra evpn mh neigh
+! debug zebra evpn mh nh
+! debug zebra vxlan
+!
+evpn mh startup-delay 1
+!
+int torm11-eth0
+ ip addr 192.168.1.2/24
+ evpn mh uplink
+!
+int torm11-eth1
+ ip addr 192.168.5.2/24
+ evpn mh uplink
+!
+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 0000000..8b0ce1d
--- /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 0000000..3dd63b4
--- /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 0000000..9532762
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm12/zebra.conf
@@ -0,0 +1,28 @@
+! debug zebra evpn mh es
+! debug zebra evpn mh mac
+! debug zebra evpn mh neigh
+! debug zebra evpn mh nh
+! debug zebra vxlan
+!
+evpn mh startup-delay 1
+!
+int torm12-eth0
+ ip addr 192.168.2.2/24
+ evpn mh uplink
+!
+int torm12-eth1
+ ip addr 192.168.6.2/24
+ evpn mh uplink
+!
+!
+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 0000000..5247dc1
--- /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 0000000..71aa91a
--- /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 0000000..6c75df7
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm21/zebra.conf
@@ -0,0 +1,29 @@
+! debug zebra evpn mh es
+! debug zebra evpn mh mac
+! debug zebra evpn mh neigh
+! debug zebra evpn mh nh
+! debug zebra vxlan
+!
+evpn mh startup-delay 1
+!
+int torm21-eth0
+ ip addr 192.168.3.2/24
+ evpn mh uplink
+!
+!
+int torm21-eth1
+ ip addr 192.168.7.2/24
+ evpn mh uplink
+!
+!
+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 0000000..ec56360
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm22/evpn.conf
@@ -0,0 +1,20 @@
+!
+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 0000000..46f330f
--- /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 0000000..4c94966
--- /dev/null
+++ b/tests/topotests/bgp_evpn_mh/torm22/zebra.conf
@@ -0,0 +1,29 @@
+! debug zebra evpn mh es
+! debug zebra evpn mh mac
+! debug zebra evpn mh neigh
+! debug zebra evpn mh nh
+! debug zebra vxlan
+!
+evpn mh startup-delay 1
+!
+int torm22-eth0
+ ip addr 192.168.4.2/24
+ evpn mh uplink
+!
+!
+int torm22-eth1
+ ip addr 192.168.8.2/24
+ evpn mh uplink
+!
+!
+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_overlay_index_gateway/PE1/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json
new file mode 100644
index 0000000..2eeebad
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_base.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json
new file mode 100644
index 0000000..419bcc3
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt2.json
@@ -0,0 +1,8 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]": null,
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json
new file mode 100644
index 0000000..2eeebad
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vni_routes_no_rt5.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:101:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":0,
+ "peerId":"10.0.1.2",
+ "path":"102",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:102:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json
new file mode 100644
index 0000000..833f986
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000..833f986
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"50.0.1.11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000..4a292bd
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv4_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } }
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json
new file mode 100644
index 0000000..3dc3fcf
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000..3dc3fcf
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"50:0:1::11",
+ "path":"111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global"
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000..6c11d89
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgp_vrf_ipv6_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf
new file mode 100644
index 0000000..63aa99a
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/bgpd.conf
@@ -0,0 +1,30 @@
+router bgp 101
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.2 remote-as 102
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.2 activate
+ advertise-all-vni
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.1
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.11 remote-as 111
+ neighbor 50:0:1::11 remote-as 111
+ !
+ address-family ipv4 unicast
+ no neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ neighbor 50:0:1::11 activate
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ advertise ipv4 unicast gateway-ip
+ advertise ipv6 unicast gateway-ip
+ exit-address-family \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf
new file mode 100644
index 0000000..99a2e89
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra.conf
@@ -0,0 +1,14 @@
+!
+log file zebra.log
+!
+ip route 10.100.0.2/32 10.0.1.2
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.1/32
+interface PE1-eth0
+ ip address 10.0.1.1/24
+!
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json
new file mode 100644
index 0000000..2dcf35d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_base.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000..2dcf35d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt2.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000..9c3091d
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv4_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+}
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json
new file mode 100644
index 0000000..229c927
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_base.json
@@ -0,0 +1,55 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000..229c927
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt2.json
@@ -0,0 +1,55 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000..94f82e6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE1/zebra_vrf_ipv6_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json
new file mode 100644
index 0000000..7b8d38e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_base.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json
new file mode 100644
index 0000000..6273b3e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt2.json
@@ -0,0 +1,68 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]": null,
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]": null,
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json
new file mode 100644
index 0000000..7b8d38e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vni_routes_no_rt5.json
@@ -0,0 +1,192 @@
+{
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[32]:[50.0.1.11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":32,
+ "ip":"50.0.1.11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:61]:[128]:[50:0:1::11]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:61",
+ "ipLen":128,
+ "ip":"50:0:1::11",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]":{
+ "prefix":"[2]:[0]:[48]:[1a:2b:3c:4d:5e:62]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":2,
+ "ethTag":0,
+ "macLen":48,
+ "mac":"1a:2b:3c:4d:5e:62",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.1]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.1]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.1",
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"RT:101:100 ET:8"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ },
+ "[3]:[0]:[32]:[10.100.0.2]":{
+ "prefix":"[3]:[0]:[32]:[10.100.0.2]",
+ "prefixLen":320,
+ "paths":[
+ [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "routeType":3,
+ "ethTag":0,
+ "ipLen":32,
+ "ip":"10.100.0.2",
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "extendedCommunity":{
+ "string":"ET:8 RT:102:100"
+ },
+ "nexthops":[
+ {
+ "ip":"10.100.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ ]
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json
new file mode 100644
index 0000000..c03d701
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_base.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000..7f1b8d2
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt2.json
@@ -0,0 +1,27 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100.0.0.21",
+ "prefixLen":32,
+ "network":"100.0.0.21\/32",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000..52e4311
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv4_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100.0.0.21/32": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json
new file mode 100644
index 0000000..1d90c9c
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_base.json
@@ -0,0 +1,28 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000..a0e63c6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt2.json
@@ -0,0 +1,28 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": [
+ {
+ "valid":null,
+ "bestpath":null,
+ "pathFrom":"external",
+ "prefix":"100::21",
+ "prefixLen":128,
+ "network":"100::21\/128",
+ "metric":0,
+ "weight":0,
+ "peerId":"10.0.1.1",
+ "path":"101 111",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "scope":"global",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000..789fe69
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgp_vrf_ipv6_no_rt5.json
@@ -0,0 +1,6 @@
+{
+ "vrfName": "vrf-blue",
+ "routerId": "10.100.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 101,
+ "routes": { "100::21/128": null } } \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf
new file mode 100644
index 0000000..59fee15
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/bgpd.conf
@@ -0,0 +1,14 @@
+router bgp 102
+ bgp router-id 10.100.0.2
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.1 remote-as 101
+ !
+ address-family l2vpn evpn
+ neighbor 10.0.1.1 activate
+ advertise-all-vni
+ enable-resolve-overlay-index
+ exit-address-family
+!
+router bgp 101 vrf vrf-blue
+ bgp router-id 10.100.0.2
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf
new file mode 100644
index 0000000..b78cdcc
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra.conf
@@ -0,0 +1,14 @@
+!
+log file zebra.log
+!
+ip route 10.100.0.1/32 10.0.1.1
+!
+vrf vrf-blue
+ vni 1000 prefix-routes-only
+ exit-vrf
+!
+interface lo
+ ip address 10.100.0.2/32
+interface PE2-eth0
+ ip address 10.0.1.2/24
+!
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json
new file mode 100644
index 0000000..b3a3640
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_base.json
@@ -0,0 +1,56 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32":[
+ {
+ "prefix":"100.0.0.21\/32",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50.0.1.11",
+ "afi":"ipv4",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json
new file mode 100644
index 0000000..996fe52
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt2.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json
new file mode 100644
index 0000000..996fe52
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv4_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50.0.1.0\/24":[
+ {
+ "prefix":"50.0.1.0\/24",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100.0.0.21\/32": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json
new file mode 100644
index 0000000..d5be22a
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_base.json
@@ -0,0 +1,56 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128":[
+ {
+ "prefix":"100::21\/128",
+ "protocol":"bgp",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":40,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"50:0:1::11",
+ "afi":"ipv6",
+ "interfaceName":"br100",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json
new file mode 100644
index 0000000..94f82e6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt2.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json
new file mode 100644
index 0000000..94f82e6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/PE2/zebra_vrf_ipv6_no_rt5.json
@@ -0,0 +1,29 @@
+{
+ "50:0:1::\/48":[
+ {
+ "prefix":"50:0:1::\/48",
+ "protocol":"connected",
+ "vrfName":"vrf-blue",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":10,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"br100",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "100::21\/128": null
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py b/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/__init__.py
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf
new file mode 100644
index 0000000..7608ec9
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 111
+ bgp router-id 10.100.0.11
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 50.0.1.1 remote-as 101
+ neighbor 50:0:1::1 remote-as 101
+ !
+ address-family ipv4 unicast
+ network 100.0.0.21/32
+ no neighbor 50:0:1::1 activate
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ network 100::21/128
+ neighbor 50:0:1::1 activate
+ exit-address-family
+
+
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf
new file mode 100644
index 0000000..c8c832e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int host1-eth0
+ ip address 50.0.1.11/24
+ ipv6 address 50:0:1::11/48
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf
new file mode 100644
index 0000000..b9f80f1
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/host2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int host2-eth0
+ ip address 50.0.1.21/24
+ ipv6 address 50:0:1::21/48
diff --git a/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py
new file mode 100755
index 0000000..86a8751
--- /dev/null
+++ b/tests/topotests/bgp_evpn_overlay_index_gateway/test_bgp_evpn_overlay_index_gateway.py
@@ -0,0 +1,414 @@
+#!/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.
+#
+
+"""
+test_bgp_evpn_overlay_index_gateway.py: Test EVPN gateway IP overlay index functionality
+Following functionality is covered:
+
+ +--------+ BGP +--------+ BGP +--------+ +--------+
+ SN1 | | IPv4/v6 | | EVPN | | | |
+ ======+ Host1 +---------+ PE1 +------+ PE2 +------+ Host2 +
+ | | | | | | | |
+ +--------+ +--------+ +--------+ +--------+
+
+ Host1 is connected to PE1 and host2 is connected to PE2
+ Host1 and PE1 have IPv4/v6 BGP sessions.
+ PE1 and PE2 gave EVPN session.
+ Host1 advertises IPv4/v6 prefixes to PE1.
+ PE1 advertises these prefixes to PE2 as EVPN type-5 routes.
+ Gateway IP for these EVPN type-5 routes is host1 IP.
+ Host1 MAC/IP is advertised by PE1 as EVPN type-2 route
+
+Following testcases are covered:
+TC_1:
+Check BGP and zebra states for above topology at PE1 and PE2.
+
+TC_2:
+Stop advertising prefixes from host1. It should withdraw type-5 routes. Check states at PE1 and PE2
+Advertise the prefixes again. Check states.
+
+TC_3:
+Shut down VxLAN interface at PE1. This should withdraw type-2 routes. Check states at PE1 and PE2.
+Enable VxLAN interface again. Check states.
+"""
+
+import os
+import sys
+import json
+from functools import partial
+import pytest
+import time
+import platform
+
+# Current Working Directory
+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 (
+ step,
+ write_test_header,
+ write_test_footer,
+ generate_support_bundle,
+)
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+PES = ["PE1", "PE2"]
+HOSTS = ["host1", "host2"]
+PE_SUFFIX = {"PE1": "1", "PE2": "2"}
+HOST_SUFFIX = {"host1": "1", "host2": "2"}
+TRIGGERS = ["base", "no_rt5", "no_rt2"]
+
+
+def build_topo(tgen):
+ # This function only purpose is to define allocation and relationship
+ # between routers and add links.
+
+ # Create routers
+ for pe in PES:
+ tgen.add_router(pe)
+ for host in HOSTS:
+ tgen.add_router(host)
+
+ krel = platform.release()
+ logger.info("Kernel version " + krel)
+
+ # Add links
+ tgen.add_link(tgen.gears["PE1"], tgen.gears["PE2"], "PE1-eth0", "PE2-eth0")
+ tgen.add_link(tgen.gears["PE1"], tgen.gears["host1"], "PE1-eth1", "host1-eth0")
+ tgen.add_link(tgen.gears["PE2"], tgen.gears["host2"], "PE2-eth1", "host2-eth0")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info(
+ "For EVPN, kernel version should be minimum 4.15. Kernel present {}".format(
+ kernelv
+ )
+ )
+ return
+
+ if topotest.version_cmp(kernelv, "4.15") == 0:
+ l3mdev_accept = 1
+ logger.info("setting net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept))
+ else:
+ l3mdev_accept = 0
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ tgen.start_topology()
+
+ # Configure MAC address for hosts as these MACs are advertised with EVPN type-2 routes
+ for name in tgen.gears:
+ if name not in HOSTS:
+ continue
+ host = tgen.net[name]
+
+ host_mac = "1a:2b:3c:4d:5e:6{}".format(HOST_SUFFIX[name])
+ host.cmd_raises("ip link set dev {}-eth0 down".format(name))
+ host.cmd_raises("ip link set dev {0}-eth0 address {1}".format(name, host_mac))
+ host.cmd_raises("ip link set dev {}-eth0 up".format(name))
+
+ # Configure PE VxLAN and Bridge interfaces
+ for name in tgen.gears:
+ if name not in PES:
+ continue
+ pe = tgen.net[name]
+
+ vtep_ip = "10.100.0.{}".format(PE_SUFFIX[name])
+ bridge_ip = "50.0.1.{}/24".format(PE_SUFFIX[name])
+ bridge_ipv6 = "50:0:1::{}/48".format(PE_SUFFIX[name])
+
+ pe.cmd_raises("ip link add vrf-blue type vrf table 10")
+ pe.cmd_raises("ip link set dev vrf-blue up")
+ pe.cmd_raises(
+ "ip link add vxlan100 type vxlan id 100 dstport 4789 local {}".format(
+ vtep_ip
+ )
+ )
+ pe.cmd_raises("ip link add name br100 type bridge stp_state 0")
+ pe.cmd_raises("ip link set dev vxlan100 master br100")
+ pe.cmd_raises("ip link set dev {}-eth1 master br100".format(name))
+ pe.cmd_raises("ip addr add {} dev br100".format(bridge_ip))
+ pe.cmd_raises("ip link set up dev br100")
+ pe.cmd_raises("ip link set up dev vxlan100")
+ pe.cmd_raises("ip link set up dev {}-eth1".format(name))
+ pe.cmd_raises("ip link set dev br100 master vrf-blue")
+ pe.cmd_raises("ip -6 addr add {} dev br100".format(bridge_ipv6))
+
+ pe.cmd_raises(
+ "ip link add vxlan1000 type vxlan id 1000 dstport 4789 local {}".format(
+ vtep_ip
+ )
+ )
+ pe.cmd_raises("ip link add name br1000 type bridge stp_state 0")
+ pe.cmd_raises("ip link set dev vxlan1000 master br100")
+ pe.cmd_raises("ip link set up dev br1000")
+ pe.cmd_raises("ip link set up dev vxlan1000")
+ pe.cmd_raises("ip link set dev br1000 master vrf-blue")
+
+ pe.cmd_raises("sysctl -w net.ipv4.ip_forward=1")
+ pe.cmd_raises("sysctl -w net.ipv6.conf.all.forwarding=1")
+ pe.cmd_raises("sysctl -w net.ipv4.udp_l3mdev_accept={}".format(l3mdev_accept))
+ pe.cmd_raises("sysctl -w net.ipv4.tcp_l3mdev_accept={}".format(l3mdev_accept))
+
+ # For all registered routers, load the zebra configuration file
+ for (name, router) in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(name))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(name))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ logger.info("Running setup_module() done")
+
+ time.sleep(10)
+
+
+def teardown_module(mod):
+ """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)
+
+
+def evpn_gateway_ip_show_op_check(trigger=" "):
+ """
+ This function checks CLI O/P for commands mentioned in show_commands for a given trigger
+ :param trigger: Should be a trigger present in TRIGGERS
+ :return: Returns a tuple (result: None for success, retmsg: Log message to be printed on failure)
+ """
+ tgen = get_topogen()
+
+ if trigger not in TRIGGERS:
+ return "Unexpected trigger", "Unexpected trigger {}".format(trigger)
+
+ show_commands = {
+ "bgp_vni_routes": "show bgp l2vpn evpn route vni 100 json",
+ "bgp_vrf_ipv4": "show bgp vrf vrf-blue ipv4 json",
+ "bgp_vrf_ipv6": "show bgp vrf vrf-blue ipv6 json",
+ "zebra_vrf_ipv4": "show ip route vrf vrf-blue json",
+ "zebra_vrf_ipv6": "show ipv6 route vrf vrf-blue json",
+ }
+
+ for (name, pe) in tgen.gears.items():
+ if name not in PES:
+ continue
+
+ for (cmd_key, command) in show_commands.items():
+ expected_op_file = "{0}/{1}/{2}_{3}.json".format(
+ CWD, name, cmd_key, trigger
+ )
+ expected_op = json.loads(open(expected_op_file).read())
+
+ test_func = partial(topotest.router_json_cmp, pe, command, expected_op)
+ ret, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{0}" JSON output mismatch for {1}'.format(name, command)
+ if result is not None:
+ return result, assertmsg
+
+ return None, "Pass"
+
+
+def test_evpn_gateway_ip_basic_topo(request):
+ """
+ Tets EVPN overlay index gateway IP functionality. VErify show O/Ps on PE1 and PE2
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Temporarily Disabled
+ tgen.set_error(
+ "%s: Failing under new micronet framework, please debug and re-enable", tc_name
+ )
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Check O/Ps for EVPN gateway IP overlay Index functionality at PE1 and PE2")
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt5(request):
+ """
+ Withdraw EVPN type-5 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ h1 = tgen.gears["host1"]
+
+ step("Withdraw type-5 routes")
+
+ h1.run(
+ 'vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "no network 100.0.0.21/32"'
+ )
+ h1.run(
+ 'vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "no network 100::21/128"'
+ )
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt5")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Advertise type-5 routes again")
+
+ h1.run(
+ 'vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv4" \
+ -c "network 100.0.0.21/32"'
+ )
+ h1.run(
+ 'vtysh -c "config t" \
+ -c "router bgp 111" \
+ -c "address-family ipv6" \
+ -c "network 100::21/128"'
+ )
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+def test_evpn_gateway_ip_flap_rt2(request):
+ """
+ Withdraw EVPN type-2 routes and check O/Ps at PE1 and PE2
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ kernelv = platform.release()
+ if topotest.version_cmp(kernelv, "4.15") < 0:
+ logger.info("For EVPN, kernel version should be minimum 4.15")
+ write_test_footer(tc_name)
+ return
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Shut down VxLAN interface at PE1 which results in withdraw of type-2 routes")
+
+ pe1 = tgen.net["PE1"]
+
+ pe1.cmd_raises("ip link set dev vxlan100 down")
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("no_rt2")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ step("Bring up VxLAN interface at PE1 and advertise type-2 routes again")
+
+ pe1.cmd_raises("ip link set dev vxlan100 up")
+
+ result, assertmsg = evpn_gateway_ip_show_op_check("base")
+ if result is not None:
+ generate_support_bundle()
+ assert result is None, assertmsg
+
+ write_test_footer(tc_name)
+
+
+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_rt5/__init__.py b/tests/topotests/bgp_evpn_rt5/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/__init__.py
diff --git a/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf
new file mode 100644
index 0000000..ccbeae6
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/r1/bgpd.conf
@@ -0,0 +1,26 @@
+! debug bgp neighbor-events
+! debug bgp updates
+! debug bgp zebra
+router bgp 65000
+ bgp router-id 192.168.100.21
+ bgp log-neighbor-changes
+ no bgp default ipv4-unicast
+ neighbor 192.168.100.41 remote-as 65000
+ neighbor 192.168.100.41 capability extended-nexthop
+ !
+ address-family l2vpn evpn
+ neighbor 192.168.100.41 activate
+ advertise-all-vni
+ exit-address-family
+!
+router bgp 65000 vrf r1-vrf-101
+ bgp router-id 192.168.102.21
+ bgp log-neighbor-changes
+ no bgp network import-check
+ address-family ipv4 unicast
+ network 192.168.102.21/32
+ exit-address-family
+ address-family l2vpn evpn
+ advertise ipv4 unicast
+ exit-address-family
+ !
diff --git a/tests/topotests/bgp_evpn_rt5/r1/zebra.conf b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf
new file mode 100644
index 0000000..4f1804c
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/r1/zebra.conf
@@ -0,0 +1,22 @@
+log stdout
+
+hostname r1
+password zebra
+
+! debug zebra vxlan
+! debug zebra kernel
+! debug zebra dplane
+! debug zebra rib
+log stdout
+vrf r1-vrf-101
+ vni 101
+ exit-vrf
+!
+interface r1-eth0
+ ip address 192.168.100.21/24
+!
+interface loop101 vrf r1-vrf-101
+ ip address 192.168.102.21/32
+!
+
+
diff --git a/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf
new file mode 100644
index 0000000..744c259
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/r2/bgpd.conf
@@ -0,0 +1,27 @@
+! debug bgp neighbor-events
+! debug bgp updates
+! debug bgp zebra
+router bgp 65000
+ bgp router-id 192.168.100.41
+ bgp log-neighbor-changes
+ no bgp default ipv4-unicast
+ neighbor 192.168.100.21 peer-group
+ neighbor 192.168.100.21 remote-as 65000
+ neighbor 192.168.100.21 capability extended-nexthop
+ !
+ address-family l2vpn evpn
+ neighbor 192.168.100.21 activate
+ advertise-all-vni
+ exit-address-family
+!
+router bgp 65000 vrf r2-vrf-101
+ bgp router-id 192.168.101.41
+ bgp log-neighbor-changes
+ no bgp network import-check
+ address-family ipv4 unicast
+ network 192.168.101.41/32
+ exit-address-family
+ address-family l2vpn evpn
+ advertise ipv4 unicast
+ exit-address-family
+ !
diff --git a/tests/topotests/bgp_evpn_rt5/r2/zebra.conf b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf
new file mode 100644
index 0000000..7d19a5b
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/r2/zebra.conf
@@ -0,0 +1,18 @@
+log stdout
+
+hostname r2
+password zebra
+
+! debug zebra vxlan
+
+vrf r2-vrf-101
+ vni 101
+ exit-vrf
+!
+interface loop101 vrf r2-vrf-101
+ ip address 192.168.101.41/32
+!
+interface r2-eth0
+ ip address 192.168.100.41/24
+!
+
diff --git a/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
new file mode 100644
index 0000000..3e2fb2b
--- /dev/null
+++ b/tests/topotests/bgp_evpn_rt5/test_bgp_evpn.py
@@ -0,0 +1,236 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_evpn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_evpn.py: Test the FRR BGP daemon with BGP IPv6 interface
+ with route advertisements on a separate netns.
+"""
+
+import os
+import sys
+import pytest
+import platform
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ 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["r1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.18") < 0:
+ logger.info(
+ 'BGP EVPN RT5 NETNS tests will not run (have kernel "{}", but it requires 4.18)'.format(
+ krel
+ )
+ )
+ return pytest.skip("Skipping BGP EVPN RT5 NETNS Test. Kernel not supported")
+
+ # create VRF vrf-101 on R1 and R2
+ # create loop101
+ cmds_vrflite = [
+ "ip link add {}-vrf-101 type vrf table 101",
+ "ip ru add oif {}-vrf-101 table 101",
+ "ip ru add iif {}-vrf-101 table 101",
+ "ip link set dev {}-vrf-101 up",
+ "ip link add loop101 type dummy",
+ "ip link set dev loop101 master {}-vrf-101",
+ "ip link set dev loop101 up",
+ ]
+
+ cmds_r2 = [ # config routing 101
+ "ip link add name bridge-101 up type bridge stp_state 0",
+ "ip link set bridge-101 master {}-vrf-101",
+ "ip link set dev bridge-101 up",
+ "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r2-eth0 local 192.168.100.41",
+ "ip link set dev vxlan-101 master bridge-101",
+ "ip link set vxlan-101 up type bridge_slave learning off flood off mcast_flood off",
+ ]
+
+ # cmds_r1_netns_method3 = [
+ # "ip link add name vxlan-{1} type vxlan id {1} dstport 4789 dev {0}-eth0 local 192.168.100.21",
+ # "ip link set dev vxlan-{1} netns {0}-vrf-{1}",
+ # "ip netns exec {0}-vrf-{1} ip li set dev lo up",
+ # "ip netns exec {0}-vrf-{1} ip link add name bridge-{1} up type bridge stp_state 0",
+ # "ip netns exec {0}-vrf-{1} ip link set dev vxlan-{1} master bridge-{1}",
+ # "ip netns exec {0}-vrf-{1} ip link set bridge-{1} up",
+ # "ip netns exec {0}-vrf-{1} ip link set vxlan-{1} up",
+ # ]
+
+ router = tgen.gears["r1"]
+
+ ns = "r1-vrf-101"
+ tgen.net["r1"].add_netns(ns)
+ tgen.net["r1"].cmd_raises("ip link add loop101 type dummy")
+ tgen.net["r1"].set_intf_netns("loop101", ns, up=True)
+
+ router = tgen.gears["r2"]
+ for cmd in cmds_vrflite:
+ logger.info("cmd to r2: " + cmd.format("r2"))
+ output = router.cmd_raises(cmd.format("r2"))
+ logger.info("result: " + output)
+
+ for cmd in cmds_r2:
+ logger.info("cmd to r2: " + cmd.format("r2"))
+ output = router.cmd_raises(cmd.format("r2"))
+ logger.info("result: " + output)
+
+ tgen.net["r1"].cmd_raises(
+ "ip link add name vxlan-101 type vxlan id 101 dstport 4789 dev r1-eth0 local 192.168.100.21"
+ )
+ tgen.net["r1"].set_intf_netns("vxlan-101", "r1-vrf-101", up=True)
+ tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set lo up")
+ tgen.net["r1"].cmd_raises(
+ "ip -n r1-vrf-101 link add name bridge-101 up type bridge stp_state 0"
+ )
+ tgen.net["r1"].cmd_raises(
+ "ip -n r1-vrf-101 link set dev vxlan-101 master bridge-101"
+ )
+ tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set bridge-101 up")
+ tgen.net["r1"].cmd_raises("ip -n r1-vrf-101 link set vxlan-101 up")
+
+ for rname, router in router_list.items():
+ if rname == "r1":
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ "--vrfwnetns",
+ )
+ else:
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.net["r1"].delete_netns("r1-vrf-101")
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ topotest.sleep(4, "waiting 4 seconds for bgp convergence")
+ # Check IPv4/IPv6 routing tables.
+ output = tgen.gears["r1"].vtysh_cmd("show bgp l2vpn evpn", isjson=False)
+ logger.info("==== result from show bgp l2vpn evpn")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd(
+ "show bgp l2vpn evpn route detail", isjson=False
+ )
+ logger.info("==== result from show bgp l2vpn evpn route detail")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101 ipv4", isjson=False)
+ logger.info("==== result from show bgp vrf r1-vrf-101 ipv4")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show bgp vrf r1-vrf-101", isjson=False)
+ logger.info("==== result from show bgp vrf r1-vrf-101 ")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show ip route vrf r1-vrf-101", isjson=False)
+ logger.info("==== result from show ip route vrf r1-vrf-101")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show evpn vni detail", isjson=False)
+ logger.info("==== result from show evpn vni detail")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show evpn next-hops vni all", isjson=False)
+ logger.info("==== result from show evpn next-hops vni all")
+ logger.info(output)
+ output = tgen.gears["r1"].vtysh_cmd("show evpn rmac vni all", isjson=False)
+ logger.info("==== result from show evpn next-hops vni all")
+ logger.info(output)
+ # Check IPv4 and IPv6 connectivity between r1 and r2 ( routing vxlan evpn)
+ pingrouter = tgen.gears["r1"]
+ logger.info(
+ "Check Ping IPv4 from R1(r1-vrf-101) to R2(r2-vrf-101 = 192.168.101.41)"
+ )
+ output = pingrouter.run("ip netns exec r1-vrf-101 ping 192.168.101.41 -f -c 1000")
+ logger.info(output)
+ if "1000 packets transmitted, 1000 received" not in output:
+ assertmsg = (
+ "expected ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) should be ok"
+ )
+ assert 0, assertmsg
+ else:
+ logger.info("Check Ping IPv4 from R1(r1-vrf-101) to R2(192.168.101.41) OK")
+
+
+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_vxlan_topo1/P1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf
new file mode 100644
index 0000000..2db7edb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/ospfd.conf
@@ -0,0 +1,13 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.20.20.20/32 area 0
+!
+int P1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int P1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf
new file mode 100644
index 0000000..95b5da8
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/P1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 10.20.20.20/32
+interface P1-eth0
+ ip address 10.20.1.2/24
+interface P1-eth1
+ ip address 10.20.2.2/24
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf
new file mode 100644
index 0000000..991a1e7
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65000
+ timers 3 9
+ bgp router-id 10.10.10.10
+ no bgp default ipv4-unicast
+ neighbor 10.30.30.30 remote-as 65000
+ neighbor 10.30.30.30 update-source lo
+ neighbor 10.30.30.30 timers 3 10
+ address-family l2vpn evpn
+ neighbor 10.30.30.30 activate
+ advertise-all-vni
+ advertise-svi-ip
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json
new file mode 100644
index 0000000..ce7915c
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/evpn.vni.json
@@ -0,0 +1,13 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.10.10.10",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":[
+ "10.30.30.30"
+ ]
+}
+
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf
new file mode 100644
index 0000000..f1c2b42
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/ospfd.conf
@@ -0,0 +1,9 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.10.10.10/32 area 0
+!
+int PE1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf
new file mode 100644
index 0000000..e269947
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE1/zebra.conf
@@ -0,0 +1,8 @@
+!
+log file zebra.log
+!
+interface lo
+ ip address 10.10.10.10/32
+interface PE1-eth1
+ ip address 10.20.1.1/24
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf
new file mode 100644
index 0000000..52f8687
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ timers bgp 3 9
+ bgp router-id 10.30.30.30
+ no bgp default ipv4-unicast
+ neighbor 10.10.10.10 remote-as 65000
+ neighbor 10.10.10.10 update-source lo
+ neighbor 10.10.10.10 timers 3 10
+ !
+ address-family l2vpn evpn
+ neighbor 10.10.10.10 activate
+ advertise-all-vni
+ advertise-svi-ip
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json
new file mode 100644
index 0000000..6c69202
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/evpn.vni.json
@@ -0,0 +1,12 @@
+{
+ "vni":101,
+ "type":"L2",
+ "vrf":"default",
+ "vxlanInterface":"vxlan101",
+ "vtepIp":"10.30.30.30",
+ "mcastGroup":"0.0.0.0",
+ "advertiseGatewayMacip":"No",
+ "numRemoteVteps":[
+ "10.10.10.10"
+ ]
+}
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf
new file mode 100644
index 0000000..065c993
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/ospfd.conf
@@ -0,0 +1,9 @@
+!
+router ospf
+ network 10.20.0.0/16 area 0
+ network 10.30.30.30/32 area 0
+!
+int PE2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf
new file mode 100644
index 0000000..9738916
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/PE2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface lo
+ ip address 10.30.30.30/32
+interface PE2-eth0
+ ip address 10.20.2.3/24
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/__init__.py b/tests/topotests/bgp_evpn_vxlan_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/__init__.py
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf
new file mode 100644
index 0000000..91fae9e
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host1/zebra.conf
@@ -0,0 +1,3 @@
+!
+int host1-eth0
+ ip address 10.10.1.55/24
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/ospfd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf b/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf
new file mode 100644
index 0000000..df9adeb
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/host2/zebra.conf
@@ -0,0 +1,3 @@
+!
+interface host2-eth0
+ ip address 10.10.1.56/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
new file mode 100755
index 0000000..5d0a326
--- /dev/null
+++ b/tests/topotests/bgp_evpn_vxlan_topo1/test_bgp_evpn_vxlan.py
@@ -0,0 +1,440 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_evpn_vxlan.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_evpn_vxlan.py: Test VXLAN EVPN MAC a route signalling over BGP.
+"""
+
+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.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("P1")
+ tgen.add_router("PE1")
+ tgen.add_router("PE2")
+ tgen.add_router("host1")
+ tgen.add_router("host2")
+
+ # Host1-PE1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["host1"])
+ switch.add_link(tgen.gears["PE1"])
+
+ # PE1-P1
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["PE1"])
+ switch.add_link(tgen.gears["P1"])
+
+ # P1-PE2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["P1"])
+ switch.add_link(tgen.gears["PE2"])
+
+ # PE2-host2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["PE2"])
+ switch.add_link(tgen.gears["host2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ p1 = tgen.gears["P1"]
+
+ # 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"
+ )
+ pe1.run("ip link set dev vxlan101 master br101")
+ pe1.run("ip link set up dev vxlan101")
+ 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"
+ )
+ pe2.run("ip link set dev vxlan101 master br101")
+ pe2.run("ip link set up dev vxlan101")
+ pe2.run("ip link set dev PE2-eth1 master br101")
+ p1.run("sysctl -w net.ipv4.ip_forward=1")
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 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 check_vni_macs_present(tgen, router, vni, maclist):
+ result = router.vtysh_cmd("show evpn mac vni {} json".format(vni), isjson=True)
+ for rname, ifname in maclist:
+ m = tgen.net.macs[(rname, ifname)]
+ if m not in result["macs"]:
+ return "MAC ({}) for interface {} on {} missing on {} from {}".format(
+ m, ifname, rname, router.name, json.dumps(result, indent=4)
+ )
+ return None
+
+
+def test_pe1_converge_evpn():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ json_file = "{}/{}/evpn.vni.json".format(CWD, pe1.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(show_vni_json_elide_ifindex, pe1, 101, expected)
+ _, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(pe1.name)
+
+ test_func = partial(
+ check_vni_macs_present,
+ tgen,
+ pe1,
+ 101,
+ (("host1", "host1-eth0"), ("host2", "host2-eth0")),
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ if result:
+ logger.warning("%s", result)
+ assert None, '"{}" missing expected MACs'.format(pe1.name)
+
+
+def test_pe2_converge_evpn():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe2 = tgen.gears["PE2"]
+ json_file = "{}/{}/evpn.vni.json".format(CWD, pe2.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(show_vni_json_elide_ifindex, pe2, 101, expected)
+ _, result = topotest.run_and_expect(test_func, None, count=45, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(pe2.name)
+ assert result is None, assertmsg
+
+ test_func = partial(
+ check_vni_macs_present,
+ tgen,
+ pe2,
+ 101,
+ (("host1", "host1-eth0"), ("host2", "host2-eth0")),
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ if result:
+ logger.warning("%s", result)
+ assert None, '"{}" missing expected MACs'.format(pe2.name)
+
+
+def mac_learn_test(host, local):
+ "check the host MAC gets learned by the VNI"
+
+ host_output = host.vtysh_cmd("show interface {}-eth0".format(host.name))
+ int_lines = host_output.splitlines()
+ for line in int_lines:
+ line_items = line.split(": ")
+ if "HWaddr" in line_items[0]:
+ mac = line_items[1]
+ break
+
+ 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", assertmsg
+
+
+def mac_test_local_remote(local, remote):
+ "test MAC transfer between local and remote"
+
+ local_output = local.vtysh_cmd("show evpn mac vni all json")
+ remote_output = remote.vtysh_cmd("show evpn mac vni all json")
+ local_output_vni = local.vtysh_cmd("show evpn vni detail json")
+ local_output_json = json.loads(local_output)
+ remote_output_json = json.loads(remote_output)
+ local_output_vni_json = json.loads(local_output_vni)
+
+ for vni in local_output_json:
+ mac_list = local_output_json[vni]["macs"]
+ for mac in mac_list:
+ if mac_list[mac]["type"] == "local" and mac_list[mac]["intf"] != "br101":
+ assertmsg = "JSON output mismatches local: {} remote: {}".format(
+ local_output_vni_json[0]["vtepIp"],
+ remote_output_json[vni]["macs"][mac]["remoteVtep"],
+ )
+ assert (
+ remote_output_json[vni]["macs"][mac]["remoteVtep"]
+ == local_output_vni_json[0]["vtepIp"]
+ ), assertmsg
+
+
+def test_learning_pe1():
+ "test MAC learning on PE1"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ host1 = tgen.gears["host1"]
+ pe1 = tgen.gears["PE1"]
+ mac_learn_test(host1, pe1)
+
+
+def test_learning_pe2():
+ "test MAC learning on PE2"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ host2 = tgen.gears["host2"]
+ pe2 = tgen.gears["PE2"]
+ mac_learn_test(host2, pe2)
+
+
+def test_local_remote_mac_pe1():
+ "Test MAC transfer PE1 local and PE2 remote"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ mac_test_local_remote(pe1, pe2)
+
+
+def test_local_remote_mac_pe2():
+ "Test MAC transfer PE2 local and PE1 remote"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["PE1"]
+ pe2 = tgen.gears["PE2"]
+ mac_test_local_remote(pe2, pe1)
+
+ # 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()
+ for line in int_lines:
+ line_items = line.split(": ")
+ if "HWaddr" in line_items[0]:
+ mac = line_items[1]
+ break
+ 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()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ 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()
+ 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_extended_optional_parameters_length/__init__.py b/tests/topotests/bgp_extended_optional_parameters_length/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/__init__.py
diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf b/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf
new file mode 100644
index 0000000..d83013c
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/r1/bgpd.conf
@@ -0,0 +1,6 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 extended-optional-parameters
+!
diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf b/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf b/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf
new file mode 100644
index 0000000..e390d6e
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 extended-optional-parameters
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf b/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf
new file mode 100644
index 0000000..dc15cf7
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py b/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py
new file mode 100644
index 0000000..e677dc6
--- /dev/null
+++ b/tests/topotests/bgp_extended_optional_parameters_length/test_bgp_extended_optional_parameters_length.py
@@ -0,0 +1,106 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if Extended Optional Parameters Length encoding format works
+if forced with a knob.
+https://datatracker.ietf.org/doc/html/rfc9072
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_extended_optional_parameters_length():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast summary json"))
+ expected = {
+ "peers": {
+ "192.168.1.2": {
+ "pfxRcd": 2,
+ "pfxSnt": 2,
+ "state": "Established",
+ "peerState": "OK",
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't converge with extended-optional-parameters"
+
+ def _bgp_extended_optional_parameters_length(router):
+ output = json.loads(router.vtysh_cmd("show bgp neighbor 192.168.1.2 json"))
+ expected = {"192.168.1.2": {"extendedOptionalParametersLength": True}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_extended_optional_parameters_length, router)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't see Extended Optional Parameters Length to be used"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json
new file mode 100644
index 0000000..5caaeab
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_delayopen_neighbor.json
@@ -0,0 +1,6 @@
+{
+ "192.168.101.2":{
+ "remoteAs":65100,
+ "bgpTimerDelayOpenTimeMsecs":240000
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json
new file mode 100644
index 0000000..3ab3588
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_established.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65000,
+ "peers":{
+ "192.168.101.2":{
+ "remoteAs":65100,
+ "state":"Established"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json
new file mode 100644
index 0000000..9a41236
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_delayopen_summary_shutdown.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65000,
+ "peers":{
+ "192.168.101.2":{
+ "remoteAs":65100,
+ "state":"Idle (Admin)"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json
new file mode 100644
index 0000000..d986071
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_shutdown_summary.json
@@ -0,0 +1,19 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.0.1",
+ "as":65000,
+ "vrfName":"default",
+ "peerCount":2,
+ "peers":{
+ "192.168.0.2":{
+ "remoteAs":65000,
+ "state":"Idle (Admin)"
+ },
+ "192.168.101.2":{
+ "remoteAs":65100,
+ "state":"Idle (Admin)"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_features/r1/bgp_summary.json b/tests/topotests/bgp_features/r1/bgp_summary.json
new file mode 100644
index 0000000..1ad7342
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgp_summary.json
@@ -0,0 +1,27 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.0.1",
+ "as":65000,
+ "vrfName":"default",
+ "peerCount":2,
+ "peers":{
+ "192.168.0.2":{
+ "hostname":"r2",
+ "remoteAs":65000,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":7,
+ "state":"Established"
+ },
+ "192.168.101.2":{
+ "hostname":"r4",
+ "remoteAs":65100,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":3,
+ "state":"Established"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_features/r1/bgpd.conf b/tests/topotests/bgp_features/r1/bgpd.conf
new file mode 100644
index 0000000..74d1993
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/bgpd.conf
@@ -0,0 +1,41 @@
+!
+hostname r1
+log file bgpd.log
+!
+router bgp 65000
+ timers bgp 3 10
+ coalesce-time 0
+ bgp router-id 192.168.0.1
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as 65000
+ neighbor 192.168.0.2 timers 3 10
+ neighbor 192.168.0.2 description Router R2 (iBGP)
+ neighbor 192.168.0.2 update-source lo
+ neighbor 192.168.0.2 timers connect 5
+ neighbor 192.168.101.2 remote-as 65100
+ neighbor 192.168.101.2 timers 3 10
+ neighbor 192.168.101.2 description Router R4 (eBGP AS 65100)
+ neighbor 192.168.101.2 timers connect 5
+ !
+ address-family ipv4 unicast
+ network 192.168.0.0/24
+ network 192.168.1.0/24
+ network 192.168.2.0/24
+ network 192.168.3.0/24
+ network 192.168.6.0/24
+ network 192.168.8.0/24
+ neighbor 192.168.101.2 route-map testmap-in in
+ neighbor 192.168.101.2 route-map testmap-out out
+ exit-address-family
+!
+!
+!
+route-map testmap-in permit 999
+!
+route-map testmap-out permit 999
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/bgp_features/r1/ip_route.json b/tests/topotests/bgp_features/r1/ip_route.json
new file mode 100644
index 0000000..34c5803
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ip_route.json
@@ -0,0 +1,364 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.101.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth3",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.0.1\/32":[
+ {
+ "prefix":"192.168.0.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.0.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.0.2\/32":[
+ {
+ "prefix":"192.168.0.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.0.3\/32":[
+ {
+ "prefix":"192.168.0.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.3.0\/24":[
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.6.0\/24":[
+ {
+ "prefix":"192.168.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.6.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.7.0\/24":[
+ {
+ "prefix":"192.168.7.0\/24",
+ "protocol":"bgp",
+ "distance":200,
+ "metric":0,
+ "nexthops":[
+ {
+ "flags":5,
+ "ip":"192.168.0.2",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true,
+ "weight":1
+ },
+ {
+ "flags":1,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.8.0\/24":[
+ {
+ "prefix":"192.168.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.101.0\/24":[
+ {
+ "prefix":"192.168.101.0\/24",
+ "protocol":"bgp",
+ "distance":20,
+ "metric":0,
+ "nexthops":[
+ {
+ "flags":0,
+ "ip":"192.168.101.2",
+ "afi":"ipv4",
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.101.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.102.0\/24":[
+ {
+ "prefix":"192.168.102.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.101.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth3",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_features/r1/ip_route_norib.json b/tests/topotests/bgp_features/r1/ip_route_norib.json
new file mode 100644
index 0000000..f6536d1
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ip_route_norib.json
@@ -0,0 +1,281 @@
+{
+ "192.168.0.1\/32":[
+ {
+ "prefix":"192.168.0.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.0.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.0.2\/32":[
+ {
+ "prefix":"192.168.0.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.0.3\/32":[
+ {
+ "prefix":"192.168.0.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.3.0\/24":[
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.6.0\/24":[
+ {
+ "prefix":"192.168.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "flags":1,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "weight":1
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.6.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.7.0\/24":[
+ {
+ "prefix":"192.168.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.8.0\/24":[
+ {
+ "prefix":"192.168.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ],
+ "192.168.101.0\/24":[
+ {
+ "prefix":"192.168.101.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_features/r1/ospf6d.conf b/tests/topotests/bgp_features/r1/ospf6d.conf
new file mode 100644
index 0000000..9afc6f4
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ospf6d.conf
@@ -0,0 +1,21 @@
+log file ospf6d.log
+!
+! debug ospf6 neighbor
+!
+interface r1-lo
+!
+interface r1-eth1
+ ipv6 ospf6 priority 10
+!
+interface r1-eth2
+ ipv6 ospf6 priority 10
+!
+router ospf6
+ ospf6 router-id 192.168.0.1
+ log-adjacency-changes
+ interface r1-lo area 0.0.0.0
+ interface r1-eth1 area 0.0.0.0
+ interface r1-eth2 area 0.0.0.0
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r1/ospf_neighbor.json b/tests/topotests/bgp_features/r1/ospf_neighbor.json
new file mode 100644
index 0000000..3b5f46d
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.2":[
+ {
+ "priority":5,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.3":[
+ {
+ "priority":5,
+ "converged":"Full"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r1/ospfd.conf b/tests/topotests/bgp_features/r1/ospfd.conf
new file mode 100644
index 0000000..aef017f
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/ospfd.conf
@@ -0,0 +1,26 @@
+log file ospfd.log
+!
+! debug ospf event
+! debug ospf zebra
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+interface r1-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.1
+ log-adjacency-changes
+ network 192.168.0.0/20 area 0.0.0.0
+ timers throttle spf 0 0 0
+ timers lsa min-arrival 10
+ timers throttle lsa all 0
+ refresh timer 10
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r1/show_bgp.json b/tests/topotests/bgp_features/r1/show_bgp.json
new file mode 100644
index 0000000..45e0e17
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/show_bgp.json
@@ -0,0 +1,350 @@
+{
+ "vrfName": "default",
+ "routerId": "192.168.0.1",
+ "defaultLocPrf": 100,
+ "localAS": 65000,
+ "routes": { "0.0.0.0/0": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"0.0.0.0",
+ "prefixLen":0,
+ "network":"0.0.0.0\/0",
+ "weight":0,
+ "peerId":"192.168.101.2",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r4",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.0.0/24": [
+ {
+ "pathFrom":"external",
+ "prefix":"192.168.0.0",
+ "prefixLen":24,
+ "network":"192.168.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.1.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "network":"192.168.1.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "network":"192.168.1.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.2.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "network":"192.168.2.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "network":"192.168.2.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.3.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.3.0",
+ "prefixLen":24,
+ "network":"192.168.3.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.3.0",
+ "prefixLen":24,
+ "network":"192.168.3.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.6.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.6.0",
+ "prefixLen":24,
+ "network":"192.168.6.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.7.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.7.0",
+ "prefixLen":24,
+ "network":"192.168.7.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.8.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.8.0",
+ "prefixLen":24,
+ "network":"192.168.8.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.8.0",
+ "prefixLen":24,
+ "network":"192.168.8.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.101.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.101.0",
+ "prefixLen":24,
+ "network":"192.168.101.0\/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.101.2",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r4",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.102.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.102.0",
+ "prefixLen":24,
+ "network":"192.168.102.0\/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.101.2",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r4",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.201.0/24": [
+ {
+ "pathFrom":"internal",
+ "prefix":"192.168.201.0",
+ "prefixLen":24,
+ "network":"192.168.201.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"65200",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.201.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.202.0/24": [
+ {
+ "pathFrom":"internal",
+ "prefix":"192.168.202.0",
+ "prefixLen":24,
+ "network":"192.168.202.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.2",
+ "path":"65200",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.201.2",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r1/show_bgp_metric_test.json b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json
new file mode 100644
index 0000000..1720572
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/show_bgp_metric_test.json
@@ -0,0 +1,57 @@
+{
+ "routerId": "192.168.0.1",
+ "routes": {
+ "192.168.1.0/24": [
+ {
+ "valid":true,
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "metric":11,
+ "nexthops":[
+ {
+ "ip":"192.168.0.2",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "metric":0,
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "used":true
+ }
+ ]
+ }
+],"192.168.101.0/24": [
+ {
+ "prefix":"192.168.101.0",
+ "prefixLen":24,
+ "metric":111,
+ "peerId":"192.168.101.2"
+ }
+],"192.168.102.0/24": [
+ {
+ "prefix":"192.168.102.0",
+ "prefixLen":24,
+ "metric":0,
+ "peerId":"192.168.101.2"
+ }
+],"192.168.201.0/24": [
+ {
+ "prefix":"192.168.201.0",
+ "prefixLen":24,
+ "metric":210,
+ "peerId":"192.168.0.2"
+ }
+],"192.168.202.0/24": [
+ {
+ "prefix":"192.168.202.0",
+ "prefixLen":24,
+ "metric":11,
+ "peerId":"192.168.0.2"
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r1/zebra.conf b/tests/topotests/bgp_features/r1/zebra.conf
new file mode 100644
index 0000000..61564f1
--- /dev/null
+++ b/tests/topotests/bgp_features/r1/zebra.conf
@@ -0,0 +1,28 @@
+!
+hostname r1
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.1/32
+ ipv6 address fc00::1/128
+!
+interface r1-eth0
+ description SW6 Stub Network
+ ip address 192.168.6.1/24
+ ipv6 address fc00:0:0:6::1/64
+!
+interface r1-eth1
+ description SW0 R1-R2 OSPF & BGP Network
+ ip address 192.168.1.1/24
+ ipv6 address fc00:0:0:1::1/64
+!
+interface r1-eth2
+ description SW2 R1-R3 OSPF Network
+ ip address 192.168.3.1/24
+ ipv6 address fc00:0:0:3::1/64
+!
+interface r1-eth3
+ description SW4 R1-R4 eBGP Network
+ ip address 192.168.101.1/24
+ ipv6 address fc00:100:0:1::1/64
+!
diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json
new file mode 100644
index 0000000..a74da03
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_delayopen_neighbor.json
@@ -0,0 +1,6 @@
+{
+ "192.168.201.2":{
+ "remoteAs":65200,
+ "bgpTimerDelayOpenTimeMsecs":60000
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json
new file mode 100644
index 0000000..c2b42ec
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_connect.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65000,
+ "peers":{
+ "192.168.201.2":{
+ "remoteAs":65200,
+ "state":"Connect"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json
new file mode 100644
index 0000000..77b6944
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_established.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65000,
+ "peers":{
+ "192.168.201.2":{
+ "remoteAs":65200,
+ "state":"Established"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json
new file mode 100644
index 0000000..8f9476a
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_delayopen_summary_shutdown.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65000,
+ "peers":{
+ "192.168.201.2":{
+ "remoteAs":65200,
+ "state":"Idle (Admin)"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json
new file mode 100644
index 0000000..b789263
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_shutdown_summary.json
@@ -0,0 +1,19 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.0.2",
+ "as":65000,
+ "vrfName":"default",
+ "peerCount":2,
+ "peers":{
+ "192.168.0.1":{
+ "remoteAs":65000,
+ "state":"Active"
+ },
+ "192.168.201.2":{
+ "remoteAs":65200,
+ "state":"Established"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_features/r2/bgp_summary.json b/tests/topotests/bgp_features/r2/bgp_summary.json
new file mode 100644
index 0000000..30e0ef4
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgp_summary.json
@@ -0,0 +1,27 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.0.2",
+ "as":65000,
+ "vrfName":"default",
+ "peerCount":2,
+ "peers":{
+ "192.168.0.1":{
+ "hostname":"r1",
+ "remoteAs":65000,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":8,
+ "state":"Established"
+ },
+ "192.168.201.2":{
+ "hostname":"r5",
+ "remoteAs":65200,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":2,
+ "state":"Established"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_features/r2/bgpd.conf b/tests/topotests/bgp_features/r2/bgpd.conf
new file mode 100644
index 0000000..99cec98
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/bgpd.conf
@@ -0,0 +1,41 @@
+!
+hostname r2
+log file bgpd.log
+!
+router bgp 65000
+ bgp router-id 192.168.0.2
+ timers bgp 3 10
+ coalesce-time 0
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as 65000
+ neighbor 192.168.0.1 timers 3 10
+ neighbor 192.168.0.1 description Router R1 (iBGP)
+ neighbor 192.168.0.1 update-source lo
+ neighbor 192.168.0.1 timers connect 5
+ neighbor 192.168.201.2 remote-as 65200
+ neighbor 192.168.201.2 timers 3 10
+ neighbor 192.168.201.2 description Router R5 (eBGP AS 65200)
+ neighbor 192.168.201.2 timers connect 5
+ !
+ address-family ipv4 unicast
+ network 192.168.0.0/24
+ network 192.168.1.0/24
+ network 192.168.2.0/24
+ network 192.168.3.0/24
+ network 192.168.7.0/24
+ network 192.168.8.0/24
+ neighbor 192.168.201.2 route-map testmap-in in
+ neighbor 192.168.201.2 route-map testmap-out out
+ exit-address-family
+!
+!
+!
+route-map testmap-in permit 999
+!
+route-map testmap-out permit 999
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/bgp_features/r2/ospf6d.conf b/tests/topotests/bgp_features/r2/ospf6d.conf
new file mode 100644
index 0000000..7fe5356
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/ospf6d.conf
@@ -0,0 +1,21 @@
+log file ospf6d.log
+!
+! debug ospf6 neighbor
+!
+interface r2-lo
+!
+interface r2-eth1
+ ipv6 ospf6 priority 5
+!
+interface r2-eth2
+ ipv6 ospf6 priority 10
+!
+router ospf6
+ ospf6 router-id 192.168.0.2
+ log-adjacency-changes
+ interface r2-lo area 0.0.0.0
+ interface r2-eth1 area 0.0.0.0
+ interface r2-eth2 area 0.0.0.0
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r2/ospf_neighbor.json b/tests/topotests/bgp_features/r2/ospf_neighbor.json
new file mode 100644
index 0000000..47bb57c
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.1":[
+ {
+ "priority":10,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.3":[
+ {
+ "priority":5,
+ "converged":"Full"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r2/ospfd.conf b/tests/topotests/bgp_features/r2/ospfd.conf
new file mode 100644
index 0000000..7f043c9
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/ospfd.conf
@@ -0,0 +1,26 @@
+log file ospfd.log
+!
+! debug ospf event
+! debug ospf zebra
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 5
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.2
+ log-adjacency-changes
+ network 192.168.0.0/20 area 0.0.0.0
+ timers throttle spf 0 0 0
+ timers lsa min-arrival 10
+ timers throttle lsa all 0
+ refresh timer 10
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r2/show_bgp.json b/tests/topotests/bgp_features/r2/show_bgp.json
new file mode 100644
index 0000000..830d5a9
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/show_bgp.json
@@ -0,0 +1,349 @@
+{
+ "vrfName": "default",
+ "routerId": "192.168.0.2",
+ "defaultLocPrf": 100,
+ "localAS": 65000,
+ "routes": { "0.0.0.0/0": [
+ {
+ "pathFrom":"internal",
+ "prefix":"0.0.0.0",
+ "prefixLen":0,
+ "network":"0.0.0.0\/0",
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.0.0/24": [
+ {
+ "pathFrom":"external",
+ "prefix":"192.168.0.0",
+ "prefixLen":24,
+ "network":"192.168.0.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.1.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "network":"192.168.1.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "network":"192.168.1.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.2.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "network":"192.168.2.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "network":"192.168.2.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.3.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.3.0",
+ "prefixLen":24,
+ "network":"192.168.3.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.3.0",
+ "prefixLen":24,
+ "network":"192.168.3.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.6.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.6.0",
+ "prefixLen":24,
+ "network":"192.168.6.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.7.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.7.0",
+ "prefixLen":24,
+ "network":"192.168.7.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.8.0/24": [
+ {
+ "valid":true,
+ "pathFrom":"internal",
+ "prefix":"192.168.8.0",
+ "prefixLen":24,
+ "network":"192.168.8.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.8.0",
+ "prefixLen":24,
+ "network":"192.168.8.0\/24",
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.101.0/24": [
+ {
+ "pathFrom":"internal",
+ "prefix":"192.168.101.0",
+ "prefixLen":24,
+ "network":"192.168.101.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.102.0/24": [
+ {
+ "pathFrom":"internal",
+ "prefix":"192.168.102.0",
+ "prefixLen":24,
+ "network":"192.168.102.0\/24",
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"192.168.0.1",
+ "path":"65100",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.2",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.201.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.201.0",
+ "prefixLen":24,
+ "network":"192.168.201.0\/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.201.2",
+ "path":"65200",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.201.2",
+ "hostname":"r5",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+],"192.168.202.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.202.0",
+ "prefixLen":24,
+ "network":"192.168.202.0\/24",
+ "metric":0,
+ "weight":0,
+ "peerId":"192.168.201.2",
+ "path":"65200",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.201.2",
+ "hostname":"r5",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r2/show_bgp_metric_test.json b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json
new file mode 100644
index 0000000..960f13d
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/show_bgp_metric_test.json
@@ -0,0 +1,57 @@
+{
+ "routerId": "192.168.0.2",
+ "routes": {
+ "192.168.2.0/24": [
+ {
+ "valid":true,
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "metric":0,
+ "nexthops":[
+ {
+ "ip":"192.168.0.1",
+ "used":true
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "metric":0,
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "used":true
+ }
+ ]
+ }
+],"192.168.101.0/24": [
+ {
+ "prefix":"192.168.101.0",
+ "prefixLen":24,
+ "metric":101,
+ "peerId":"192.168.0.1"
+ }
+],"192.168.102.0/24": [
+ {
+ "prefix":"192.168.102.0",
+ "prefixLen":24,
+ "metric":0,
+ "peerId":"192.168.0.1"
+ }
+],"192.168.201.0/24": [
+ {
+ "prefix":"192.168.201.0",
+ "prefixLen":24,
+ "metric":222,
+ "peerId":"192.168.201.2"
+ }
+],"192.168.202.0/24": [
+ {
+ "prefix":"192.168.202.0",
+ "prefixLen":24,
+ "metric":0,
+ "peerId":"192.168.201.2"
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r2/zebra.conf b/tests/topotests/bgp_features/r2/zebra.conf
new file mode 100644
index 0000000..1d427da
--- /dev/null
+++ b/tests/topotests/bgp_features/r2/zebra.conf
@@ -0,0 +1,28 @@
+!
+hostname r2
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.2/32
+ ipv6 address fc00::2/128
+!
+interface r2-eth0
+ description SW7 Stub Network
+ ip address 192.168.7.1/24
+ ipv6 address fc00:0:0:7::1/64
+!
+interface r2-eth1
+ description SW0 R1-R2 OSPF & BGP Network
+ ip address 192.168.1.2/24
+ ipv6 address fc00:0:0:1::2/64
+!
+interface r2-eth2
+ description SW1 R2-R3 OSPF Network
+ ip address 192.168.2.1/24
+ ipv6 address fc00:0:0:2::1/64
+!
+interface r2-eth3
+ description SW5 R2-R5 eBGP Network
+ ip address 192.168.201.1/24
+ ipv6 address fc00:200:0:1::1/64
+!
diff --git a/tests/topotests/bgp_features/r3/bgp_summary.json b/tests/topotests/bgp_features/r3/bgp_summary.json
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/bgp_summary.json
diff --git a/tests/topotests/bgp_features/r3/ospf6d.conf b/tests/topotests/bgp_features/r3/ospf6d.conf
new file mode 100644
index 0000000..07325b6
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/ospf6d.conf
@@ -0,0 +1,21 @@
+log file ospf6d.log
+!
+! debug ospf6 neighbor
+!
+interface r3-lo
+!
+interface r3-eth1
+ ipv6 ospf6 priority 5
+!
+interface r3-eth2
+ ipv6 ospf6 priority 5
+!
+router ospf6
+ ospf6 router-id 192.168.0.3
+ log-adjacency-changes
+ interface r3-lo area 0.0.0.0
+ interface r3-eth1 area 0.0.0.0
+ interface r3-eth2 area 0.0.0.0
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r3/ospf_neighbor.json b/tests/topotests/bgp_features/r3/ospf_neighbor.json
new file mode 100644
index 0000000..b84974c
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/ospf_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "neighbors":{
+ "192.168.0.1":[
+ {
+ "priority":10,
+ "converged":"Full"
+ }
+ ],
+ "192.168.0.2":[
+ {
+ "priority":10,
+ "converged":"Full"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_features/r3/ospfd.conf b/tests/topotests/bgp_features/r3/ospfd.conf
new file mode 100644
index 0000000..c3399fd
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/ospfd.conf
@@ -0,0 +1,26 @@
+log file ospfd.log
+!
+! debug ospf event
+! debug ospf zebra
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 5
+!
+int r3-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 5
+!
+router ospf
+ ospf router-id 192.168.0.3
+ log-adjacency-changes
+ network 192.168.0.0/20 area 0.0.0.0
+ timers throttle spf 0 0 0
+ timers lsa min-arrival 10
+ timers throttle lsa all 0
+ refresh timer 10
+!
+line vty
+!
diff --git a/tests/topotests/bgp_features/r3/zebra.conf b/tests/topotests/bgp_features/r3/zebra.conf
new file mode 100644
index 0000000..62ba04d
--- /dev/null
+++ b/tests/topotests/bgp_features/r3/zebra.conf
@@ -0,0 +1,23 @@
+!
+hostname r3
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.3/32
+ ipv6 address fc00::3/128
+!
+interface r3-eth0
+ description SW8 Stub Network
+ ip address 192.168.8.1/24
+ ipv6 address fc00:0:0:8::1/64
+!
+interface r3-eth1
+ description SW1 R2-R3 OSPF Network
+ ip address 192.168.2.2/24
+ ipv6 address fc00:0:0:2::2/64
+!
+interface r3-eth2
+ description SW2 R1-R3 OSPF Network
+ ip address 192.168.3.2/24
+ ipv6 address fc00:0:0:3::2/64
+!
diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json
new file mode 100644
index 0000000..85caf55
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_established.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65100,
+ "peers":{
+ "192.168.101.1":{
+ "remoteAs":65000,
+ "state":"Established"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json
new file mode 100644
index 0000000..cf784d8
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/bgp_delayopen_summary_shutdown.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65100,
+ "peers":{
+ "192.168.101.1":{
+ "remoteAs":65000,
+ "state":"Idle (Admin)"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json
new file mode 100644
index 0000000..ede4dd6
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/bgp_shutdown_summary.json
@@ -0,0 +1,14 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.100.1",
+ "as":65100,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "192.168.101.1":{
+ "remoteAs":65000,
+ "state":"Active" }
+ },
+ "totalPeers":1
+}
+}
diff --git a/tests/topotests/bgp_features/r4/bgp_summary.json b/tests/topotests/bgp_features/r4/bgp_summary.json
new file mode 100644
index 0000000..c0dfe78
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/bgp_summary.json
@@ -0,0 +1,18 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.100.1",
+ "as":65100,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "192.168.101.1":{
+ "hostname":"r1",
+ "remoteAs":65000,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":6,
+ "state":"Established" }
+ },
+ "totalPeers":1
+}
+}
diff --git a/tests/topotests/bgp_features/r4/bgpd.conf b/tests/topotests/bgp_features/r4/bgpd.conf
new file mode 100644
index 0000000..cdf8f4e
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/bgpd.conf
@@ -0,0 +1,34 @@
+!
+hostname r4
+log file bgpd.log
+!
+router bgp 65100
+ bgp router-id 192.168.100.1
+ timers bgp 3 10
+ coalesce-time 0
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ neighbor 192.168.101.1 remote-as 65000
+ neighbor 192.168.101.1 timers 3 10
+ neighbor 192.168.101.1 description Router R1 (eBGP AS 65000)
+ neighbor 192.168.101.1 timers connect 5
+ !
+ address-family ipv4 unicast
+ network 192.168.100.0/24
+ network 192.168.101.0/24
+ network 192.168.102.0/24
+ neighbor 192.168.101.1 default-originate
+ neighbor 192.168.101.1 route-map testmap-in in
+ neighbor 192.168.101.1 route-map testmap-out out
+ exit-address-family
+!
+!
+!
+route-map testmap-in permit 999
+!
+route-map testmap-out permit 999
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/bgp_features/r4/show_bgp_metric_test.json b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json
new file mode 100644
index 0000000..2329498
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/show_bgp_metric_test.json
@@ -0,0 +1,27 @@
+{
+ "routerId": "192.168.100.1",
+ "localAS": 65100,
+ "routes": {
+ "192.168.1.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.1.0",
+ "prefixLen":24,
+ "network":"192.168.1.0\/24",
+ "metric":1011,
+ "weight":0,
+ "peerId":"192.168.101.1",
+ "path":"65000",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.101.1",
+ "hostname":"r1",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r4/zebra.conf b/tests/topotests/bgp_features/r4/zebra.conf
new file mode 100644
index 0000000..08e3e1a
--- /dev/null
+++ b/tests/topotests/bgp_features/r4/zebra.conf
@@ -0,0 +1,18 @@
+!
+hostname r4
+log file zebra.log
+!
+interface lo
+ ip address 192.168.100.1/32
+ ipv6 address fc00:100::1/128
+!
+interface r4-eth0
+ description SW5 Stub Network
+ ip address 192.168.102.1/24
+ ipv6 address fc00:100:0:2::1/64
+!
+interface r4-eth1
+ description SW0 R1-R2 OSPF & BGP Network
+ ip address 192.168.101.2/24
+ ipv6 address fc00:100:0:1::2/64
+!
diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json
new file mode 100644
index 0000000..4b97254
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgp_delayopen_neighbor.json
@@ -0,0 +1,6 @@
+{
+ "192.168.201.1":{
+ "remoteAs":65000,
+ "bgpTimerDelayOpenTimeMsecs":30000
+ }
+}
diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json
new file mode 100644
index 0000000..d7b4e77
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_connect.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65200,
+ "peers":{
+ "192.168.201.1":{
+ "remoteAs":65000,
+ "state":"Connect"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json
new file mode 100644
index 0000000..15cfb19
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_established.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65200,
+ "peers":{
+ "192.168.201.1":{
+ "remoteAs":65000,
+ "state":"Established"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json
new file mode 100644
index 0000000..94aceba
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgp_delayopen_summary_shutdown.json
@@ -0,0 +1,11 @@
+{
+"ipv4Unicast":{
+ "as":65200,
+ "peers":{
+ "192.168.201.1":{
+ "remoteAs":65000,
+ "state":"Idle (Admin)"
+ }
+ }
+}
+}
diff --git a/tests/topotests/bgp_features/r5/bgp_summary.json b/tests/topotests/bgp_features/r5/bgp_summary.json
new file mode 100644
index 0000000..b854af5
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgp_summary.json
@@ -0,0 +1,19 @@
+{
+"ipv4Unicast":{
+ "routerId":"192.168.200.1",
+ "as":65200,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "192.168.201.1":{
+ "hostname":"r2",
+ "remoteAs":65000,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":6,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+}
+}
diff --git a/tests/topotests/bgp_features/r5/bgpd.conf b/tests/topotests/bgp_features/r5/bgpd.conf
new file mode 100644
index 0000000..6368fed
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/bgpd.conf
@@ -0,0 +1,34 @@
+!
+hostname r5
+log file bgpd.log
+!
+router bgp 65200
+ bgp router-id 192.168.200.1
+ timers bgp 3 10
+ coalesce-time 0
+ bgp log-neighbor-changes
+ no bgp ebgp-requires-policy
+ neighbor 192.168.201.1 remote-as 65000
+ neighbor 192.168.201.1 timers 3 10
+ neighbor 192.168.201.1 description Router R2 (eBGP AS 65000)
+ neighbor 192.168.201.1 timers connect 5
+ !
+ address-family ipv4 unicast
+ network 192.168.200.0/24
+ network 192.168.201.0/24
+ network 192.168.202.0/24
+ neighbor 192.168.101.1 default-originate
+ neighbor 192.168.201.1 route-map testmap-in in
+ neighbor 192.168.201.1 route-map testmap-out out
+ exit-address-family
+!
+!
+!
+route-map testmap-in permit 999
+!
+route-map testmap-out permit 999
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/bgp_features/r5/show_bgp_metric_test.json b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json
new file mode 100644
index 0000000..e6608b4
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/show_bgp_metric_test.json
@@ -0,0 +1,27 @@
+{
+ "routerId": "192.168.200.1",
+ "localAS": 65200,
+ "routes": {
+ "192.168.2.0/24": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "pathFrom":"external",
+ "prefix":"192.168.2.0",
+ "prefixLen":24,
+ "network":"192.168.2.0\/24",
+ "metric":2022,
+ "weight":0,
+ "peerId":"192.168.201.1",
+ "path":"65000",
+ "origin":"IGP",
+ "nexthops":[
+ {
+ "ip":"192.168.201.1",
+ "hostname":"r2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+] } }
diff --git a/tests/topotests/bgp_features/r5/zebra.conf b/tests/topotests/bgp_features/r5/zebra.conf
new file mode 100644
index 0000000..4d9064a
--- /dev/null
+++ b/tests/topotests/bgp_features/r5/zebra.conf
@@ -0,0 +1,18 @@
+!
+hostname r5
+log file zebra.log
+!
+interface lo
+ ip address 192.168.200.1/32
+ ipv6 address fc00:200::1/128
+!
+interface r5-eth0
+ description SW6 Stub Network
+ ip address 192.168.202.1/24
+ ipv6 address fc00:200:0:2::1/64
+!
+interface r5-eth1
+ description SW0 R1-R2 OSPF & BGP Network
+ ip address 192.168.201.2/24
+ ipv6 address fc00:200:0:1::2/64
+!
diff --git a/tests/topotests/bgp_features/test_bgp_features.dot b/tests/topotests/bgp_features/test_bgp_features.dot
new file mode 100644
index 0000000..70b126c
--- /dev/null
+++ b/tests/topotests/bgp_features/test_bgp_features.dot
@@ -0,0 +1,83 @@
+## GraphViz file for test_all_protocol_startup
+##
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## No protocol: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## OSPFv2: #32b835 Green
+## OSPFv3: #19e3d9 Cyan
+## ISIS IPv4 #33ff99 light green
+## ISIS IPv6 #9a81ec purple
+## BGP IPv4 #eee3d3 beige
+## BGP IPv6 #fdff00 yellow
+## LDP IPv4 #fedbe2 light pink
+##### Colors (see http://www.color-hex.com/)
+
+graph test_all_protocol_startup {
+ overlap=false;
+ constraint=false;
+
+ // title
+ labelloc="t";
+ label="Test Topologoy BGP Features";
+ rankdir = TB;
+
+ ######################
+ # Routers
+ ######################
+
+ # Main FRR Router with all protocols
+ R4 [shape=doubleoctagon, label="R4 FRR\nAS 65100\nlo: 192.168.100.1/32\nfc00:100::1/128", fillcolor="#f08080", style=filled];
+ R5 [shape=doubleoctagon, label="R5 FRR\nAS 65200\nlo: 192.168.200.1/32\nfc00:200::1/128", fillcolor="#f08080", style=filled];
+ #{ rank = same {R4, R5}}
+
+ R1 [shape=doubleoctagon, label="R1 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled];
+ R2 [shape=doubleoctagon, label="R2 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled];
+ #{ rank = same { R1, R2}}
+
+ R3 [shape=doubleoctagon, label="R3 FRR\nAS 65000\nlo: 192.168.0.1/32\nfc00::1/128", fillcolor="#f08080", style=filled];
+
+ ######################
+ # Network Lists
+ ######################
+
+ SW1_R1_R2 [label="SW1 OSPF & iBGP\n192.168.1.0/24\nfc00:0:0:1::/64", fillcolor="#32b835", style=filled];
+ SW2_R2_R3 [label="SW2 OSPF\n192.168.2.0/24\nfc00:0:0:2::/64", fillcolor="#19e3d9", style=filled];
+ SW3_R3_R1 [label="SW3 OSPF\n192.168.3.0/24\nfc00:0:0:3::/64", fillcolor="#19e3d9", style=filled];
+
+ SW4_R4_R1 [label="SW4 eBGP\n192.168.101.0/24\nfc00:100:0:1::/64", fillcolor="#fedbe2", style=filled];
+ SW5_R5_R2 [label="SW5 eBGP\n192.168.201.0/24\nfc00:200:0:1::/64", fillcolor="#fedbe2", style=filled];
+ #{ rank = same {SW4_R4_R1, SW5_R5_R2}}
+
+ SW6_STUB_R1 [label="SW6\n192.168.6.0/24\nfc00:0:0:6::/64", fillcolor="#d0e0d0", style=filled];
+ SW7_STUB_R2 [label="SW7\n192.168.7.0/24\nfc00:0:0:7::/64", fillcolor="#d0e0d0", style=filled];
+ SW8_STUB_R3 [label="SW8\n192.168.8.0/24\nfc00:0:0:8::/64", fillcolor="#d0e0d0", style=filled];
+ SW9_STUB_R4 [label="SW9\n192.168.102.0/24\nfc00:100:0:2::/64", fillcolor="#d0e0d0", style=filled];
+ SW10_STUB_R5 [label="SW10\n192.168.202.0/24\nfc00:200:0:2::/64", fillcolor="#d0e0d0", style=filled];
+
+
+ ######################
+ # Network Connections
+ ######################
+
+ R1 -- SW6_STUB_R1 [label = "eth0\n.1\n::1"];
+ R2 -- SW7_STUB_R2 [label = "eth0\n.1\n::1"];
+ R3 -- SW8_STUB_R3 [label = "eth0\n.1\n::1"];
+ R4 -- SW9_STUB_R4 [label = "eth0\n.1\n::1"];
+ R5 -- SW10_STUB_R5 [label = "eth0\n.1\n::1"];
+
+ R1 -- SW1_R1_R2 [label = "eth1\n.1\n::1"];
+ R1 -- SW3_R3_R1 [label = "eth2\n.1\n::1"];
+ R2 -- SW1_R1_R2 [label = "eth1\n.2\n::2"];
+ R2 -- SW2_R2_R3 [label = "eth2\n.1\n::1"];
+ R3 -- SW2_R2_R3 [label = "eth1\n.2\n::2"];
+ R3 -- SW3_R3_R1 [label = "eth2\n.2\n::2"];
+
+ R1 -- SW4_R4_R1 [label = "eth3\n.1\n::1"];
+ R2 -- SW5_R5_R2 [label = "eth3\n.1\n::1"];
+ R4 -- SW4_R4_R1 [label = "eth1\n.2\n::2"];
+ R5 -- SW5_R5_R2 [label = "eth1\n.2\n::2"];
+
+}
diff --git a/tests/topotests/bgp_features/test_bgp_features.pdf b/tests/topotests/bgp_features/test_bgp_features.pdf
new file mode 100644
index 0000000..cb52a54
--- /dev/null
+++ b/tests/topotests/bgp_features/test_bgp_features.pdf
Binary files differ
diff --git a/tests/topotests/bgp_features/test_bgp_features.py b/tests/topotests/bgp_features/test_bgp_features.py
new file mode 100644
index 0000000..ab44ba3
--- /dev/null
+++ b/tests/topotests/bgp_features/test_bgp_features.py
@@ -0,0 +1,1102 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_features.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_bgp_features.py: Test various BGP features.
+"""
+
+import json
+import functools
+import os
+import sys
+import pytest
+import re
+import time
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+#####################################################
+#
+# Network Topology Definition
+#
+#####################################################
+
+
+def build_topo(tgen):
+ for rtrNum in range(1, 6):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # create ExaBGP peers
+ for peer_num in range(1, 5):
+ tgen.add_exabgp_peer(
+ "peer{}".format(peer_num),
+ ip="192.168.101.{}".format(peer_num + 2),
+ defaultRoute="via 192.168.101.1",
+ )
+
+ # Setup Switches and connections
+ for swNum in range(1, 11):
+ tgen.add_switch("sw{}".format(swNum))
+
+ # Add connections to stub switches
+ tgen.gears["r1"].add_link(tgen.gears["sw6"])
+ tgen.gears["r2"].add_link(tgen.gears["sw7"])
+ tgen.gears["r3"].add_link(tgen.gears["sw8"])
+ tgen.gears["r4"].add_link(tgen.gears["sw9"])
+ tgen.gears["r5"].add_link(tgen.gears["sw10"])
+
+ # Add connections to R1-R2-R3 core
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw3"])
+ tgen.gears["r2"].add_link(tgen.gears["sw1"])
+ tgen.gears["r2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r3"].add_link(tgen.gears["sw2"])
+ tgen.gears["r3"].add_link(tgen.gears["sw3"])
+
+ # Add connections to external R4/R5 Routers
+ tgen.gears["r1"].add_link(tgen.gears["sw4"])
+ tgen.gears["r4"].add_link(tgen.gears["sw4"])
+ tgen.gears["r2"].add_link(tgen.gears["sw5"])
+ tgen.gears["r5"].add_link(tgen.gears["sw5"])
+
+ # Add ExaBGP peers to sw4
+ tgen.gears["peer1"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer2"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer3"].add_link(tgen.gears["sw4"])
+ tgen.gears["peer4"].add_link(tgen.gears["sw4"])
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if os.path.exists(os.path.join(CWD, "{}/bgpd.conf".format(rname))):
+ logger.info("{} uses BGPd".format(rname))
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ if os.path.exists(os.path.join(CWD, "{}/ospfd.conf".format(rname))):
+ logger.info("{} uses OSPFd".format(rname))
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ if os.path.exists(os.path.join(CWD, "{}/ospf6d.conf".format(rname))):
+ logger.info("{} uses OSPF6d".format(rname))
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+ router.start()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 topology convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check Router r1, r2 & r3 OSPF
+ for rtrNum in range(1, 4):
+ logger.info("Checking OSPFv2 convergence on router r{}".format(rtrNum))
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/ospf_neighbor.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R{} did not converge".format(rtrNum)
+ assert res is None, assertmsg
+
+
+def test_bgp_convergence():
+ "Test for BGP topology convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check Router r1 & r2 BGP
+ for rtrNum in [1, 2, 4, 5]:
+ logger.info("Checking BGP IPv4 convergence on router r{}".format(rtrNum))
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "BGP router R{} did not converge".format(rtrNum)
+ assert res is None, assertmsg
+
+ # tgen.mininet_cli()
+
+
+def test_bgp_shutdown():
+ "Test BGP instance shutdown"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "bgp shutdown message ABCDabcd"'
+ )
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2, 4]:
+ logger.info(
+ "Checking BGP Summary after shutdown of R1 BGP on router r{}".format(rtrNum)
+ )
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/bgp_shutdown_summary.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(
+ rtrNum
+ )
+ assert res is None, assertmsg
+
+
+def test_bgp_shutdown_message():
+ "Test BGP Peer Shutdown Message"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rtrNum in [2, 4]:
+ logger.info("Checking BGP shutdown received on router r{}".format(rtrNum))
+
+ shut_message = tgen.net["r{}".format(rtrNum)].cmd(
+ 'tail bgpd.log | grep "NOTIFICATION.*Cease/Administrative Shutdown"'
+ )
+ assertmsg = "BGP shutdown message not received on router R{}".format(rtrNum)
+ assert shut_message != "", assertmsg
+
+ assertmsg = "Incorrect BGP shutdown message received on router R{}".format(
+ rtrNum
+ )
+ assert "ABCDabcd" in shut_message, assertmsg
+
+ # tgen.mininet_cli()
+
+
+def test_bgp_no_shutdown():
+ "Test BGP instance no shutdown"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "router bgp 65000" -c "no bgp shutdown"')
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2, 4]:
+ logger.info(
+ "Checking BGP Summary after removing bgp shutdown on router r1 on router r{}".format(
+ rtrNum
+ )
+ )
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "BGP sessions on router R{} are in incorrect state (not down as expected?)".format(
+ rtrNum
+ )
+ assert res is None, assertmsg
+
+
+def test_bgp_metric_config():
+ "Test BGP Changing metric values in route-maps"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring bgp route-maps on router r1 and r2 to update metric")
+
+ # # Adding the following configuration to r1:
+ # router bgp 65000
+ # address-family ipv4 unicast
+ # neighbor 192.168.0.2 route-map addmetric-in in
+ # neighbor 192.168.0.2 route-map addmetric-out out
+ # neighbor 192.168.101.2 route-map setmetric-in in
+ # neighbor 192.168.101.2 route-map setmetric-out out
+ # exit-address-family
+ # !
+ # ip prefix-list net1 seq 10 permit 192.168.101.0/24
+ # ip prefix-list net2 seq 20 permit 192.168.1.0/24
+ # !
+ # route-map setmetric-in permit 10
+ # match ip address prefix-list net1
+ # set metric 111
+ # !
+ # route-map setmetric-in permit 20
+ # !
+ # route-map setmetric-out permit 10
+ # match ip address prefix-list net2
+ # set metric 1011
+ # !
+ # route-map setmetric-out permit 20
+ # !
+ # route-map addmetric-in permit 10
+ # set metric +11
+ # !
+ # route-map addmetric-out permit 10
+ # set metric +12
+ # !
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" '
+ + '-c "address-family ipv4 unicast" '
+ + '-c "neighbor 192.168.0.2 route-map addmetric-in in" '
+ + '-c "neighbor 192.168.0.2 route-map addmetric-out out" '
+ + '-c "neighbor 192.168.101.2 route-map setmetric-in in" '
+ + '-c "neighbor 192.168.101.2 route-map setmetric-out out" '
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "ip prefix-list net1 seq 10 permit 192.168.101.0/24" '
+ + '-c "ip prefix-list net2 seq 20 permit 192.168.1.0/24"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map setmetric-in permit 10" '
+ + '-c "match ip address prefix-list net1" '
+ + '-c "set metric 111" '
+ + '-c "route-map setmetric-in permit 20"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map setmetric-out permit 10" '
+ + '-c "match ip address prefix-list net2" '
+ + '-c "set metric 1011" '
+ + '-c "route-map setmetric-out permit 20"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map addmetric-in permit 10" '
+ + '-c "set metric +11"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map addmetric-out permit 10" '
+ + '-c "set metric +12"'
+ )
+
+ # # Adding the following configuration to r2:
+ # router bgp 65000
+ # address-family ipv4 unicast
+ # neighbor 192.168.0.1 route-map subtractmetric-in in
+ # neighbor 192.168.0.1 route-map subtractmetric-out out
+ # neighbor 192.168.201.2 route-map setmetric-in in
+ # neighbor 192.168.201.2 route-map setmetric-out out
+ # exit-address-family
+ # !
+ # ip prefix-list net1 seq 10 permit 192.168.201.0/24
+ # ip prefix-list net2 seq 20 permit 192.168.2.0/24
+ # !
+ # route-map setmetric-in permit 10
+ # match ip address prefix-list net1
+ # set metric 222
+ # !
+ # route-map setmetric-in permit 20
+ # !
+ # route-map setmetric-out permit 10
+ # match ip address prefix-list net2
+ # set metric 2022
+ # !
+ # route-map setmetric-out permit 20
+ # !
+ # route-map subtractmetric-in permit 10
+ # set metric -22
+ # !
+ # route-map subtractmetric-out permit 10
+ # set metric -23
+ # !
+
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" '
+ + '-c "address-family ipv4 unicast" '
+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-in in" '
+ + '-c "neighbor 192.168.0.1 route-map subtractmetric-out out" '
+ + '-c "neighbor 192.168.201.2 route-map setmetric-in in" '
+ + '-c "neighbor 192.168.201.2 route-map setmetric-out out" '
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "ip prefix-list net1 seq 10 permit 192.168.201.0/24" '
+ + '-c "ip prefix-list net2 seq 20 permit 192.168.2.0/24" '
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map setmetric-in permit 10" '
+ + '-c "match ip address prefix-list net1" '
+ + '-c "set metric 222" '
+ + '-c "route-map setmetric-in permit 20"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map setmetric-out permit 10" '
+ + '-c "match ip address prefix-list net2" '
+ + '-c "set metric 2022" '
+ + '-c "route-map setmetric-out permit 20"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map subtractmetric-in permit 10" '
+ + '-c "set metric -22"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "route-map subtractmetric-out permit 10" '
+ + '-c "set metric -23"'
+ )
+
+ # Clear IN the bgp neighbors to make sure the route-maps are applied
+ tgen.net["r1"].cmd(
+ 'vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"'
+ )
+
+ # tgen.mininet_cli()
+
+ # Checking BGP config - should show the bgp metric settings in the route-maps
+ logger.info("Checking BGP configuration for correct 'set metric' values")
+
+ setmetric111 = (
+ tgen.net["r1"].cmd('vtysh -c "show running" | grep "^ set metric 111"').rstrip()
+ )
+ assertmsg = (
+ "'set metric 111' configuration applied to R1, but not visible in configuration"
+ )
+ assert setmetric111 == " set metric 111", assertmsg
+
+ setmetric222 = (
+ tgen.net["r2"].cmd('vtysh -c "show running" | grep "^ set metric 222"').rstrip()
+ )
+ assertmsg = (
+ "'set metric 222' configuration applied to R2, but not visible in configuration"
+ )
+ assert setmetric222 == " set metric 222", assertmsg
+
+
+def test_bgp_metric_add_config():
+ "Test BGP Changing metric values in route-maps"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking BGP configuration for correct 'set metric' ADD value")
+
+ setmetricP11 = (
+ tgen.net["r1"].cmd('vtysh -c "show running" | grep "^ set metric +11"').rstrip()
+ )
+ assertmsg = (
+ "'set metric +11' configuration applied to R1, but not visible in configuration"
+ )
+ assert setmetricP11 == " set metric +11", assertmsg
+
+
+def test_bgp_metric_subtract_config():
+ "Test BGP Changing metric values in route-maps"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking BGP configuration for correct 'set metric' SUBTRACT value")
+
+ setmetricM22 = (
+ tgen.net["r2"].cmd('vtysh -c "show running" | grep "^ set metric -22"').rstrip()
+ )
+ assertmsg = (
+ "'set metric -22' configuration applied to R2, but not visible in configuration"
+ )
+ assert setmetricM22 == " set metric -22", assertmsg
+
+
+def test_bgp_set_metric():
+ "Test setting metrics"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Test absolute metric")
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2, 4, 5]:
+ logger.info("Checking metrics of BGP router on r{}".format(rtrNum))
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/show_bgp_metric_test.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "BGP metrics on router r{} wrong".format(rtrNum)
+ assert res is None, assertmsg
+
+
+def test_bgp_remove_metric_rmaps():
+ "Test removing route-maps with metric changes again"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Test absolute metric")
+
+ # Remove metric route-maps and relevant comfiguration
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" '
+ + '-c "address-family ipv4 unicast" '
+ + '-c "no neighbor 192.168.0.2 route-map addmetric-in in" '
+ + '-c "no neighbor 192.168.0.2 route-map addmetric-out out" '
+ + '-c "no neighbor 192.168.101.2 route-map setmetric-in in" '
+ + '-c "no neighbor 192.168.101.2 route-map setmetric-out out" '
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "no ip prefix-list net1" '
+ + '-c "no ip prefix-list net2"'
+ )
+ tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ')
+ tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ')
+ tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ')
+ tgen.net["r1"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ')
+
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" '
+ + '-c "address-family ipv4 unicast" '
+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-in in" '
+ + '-c "no neighbor 192.168.0.1 route-map subtractmetric-out out" '
+ + '-c "no neighbor 192.168.201.2 route-map setmetric-in in" '
+ + '-c "no neighbor 192.168.201.2 route-map setmetric-out out" '
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" '
+ + '-c "no ip prefix-list net1" '
+ + '-c "no ip prefix-list net2" '
+ )
+ tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-in" ')
+ tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map setmetric-out" ')
+ tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-in" ')
+ tgen.net["r2"].cmd('vtysh -c "conf t" ' + '-c "no route-map addmetric-out" ')
+
+ # Clear IN the bgp neighbors to make sure the route-maps are applied
+ tgen.net["r1"].cmd(
+ 'vtysh -c "clear ip bgp 192.168.0.2 in" ' + '-c "clear ip bgp 192.168.101.2 in"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "clear ip bgp 192.168.0.1 in" ' + '-c "clear ip bgp 192.168.201.2 in"'
+ )
+
+ # tgen.mininet_cli()
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2]:
+ logger.info("Checking metrics of BGP router on r{}".format(rtrNum))
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/show_bgp.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "BGP routes on router r{} are wrong after removing metric route-maps".format(
+ rtrNum
+ )
+ assert res is None, assertmsg
+
+
+def test_bgp_norib():
+ "Test BGP disable RIB (Zebra) Route Install"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring 'bgp no-rib' on router r1")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "bgp no-rib"')
+
+ # Checking BGP config - should show the "bgp no-rib" under the router bgp section
+ logger.info("Checking BGP configuration for 'bgp no-rib'")
+
+ norib_cfg = (
+ tgen.net["r1"].cmd('vtysh -c "show running bgpd" | grep "^bgp no-rib"').rstrip()
+ )
+
+ assertmsg = "'bgp no-rib' configuration applied, but not visible in configuration"
+ assert norib_cfg == "bgp no-rib", assertmsg
+
+
+def test_bgp_norib_routes():
+ "Test Routes in Zebra and BGP with the 'bgp-norib' configuration"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Checking local BGP routes - they need to be gone from Zebra
+ logger.info("Checking Zebra routes after removing bgp shutdown on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ip_route_norib.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "Zebra IPv4 Routes after configuring 'bgp no-rib' (There should be no BGP routes in Zebra anymore)"
+ assert res is None, assertmsg
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2, 4]:
+ logger.info(
+ "Checking BGP Summary after 'bgp no-rib' on router r1 on router r{}".format(
+ rtrNum
+ )
+ )
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "BGP sessions on router R{} has incorrect routes after adding 'bgp no-rib on r1'".format(
+ rtrNum
+ )
+ assert res is None, assertmsg
+
+ # tgen.mininet_cli()
+
+
+def test_bgp_disable_norib():
+ "Test BGP disabling the no-RIB (Zebra) Route Install"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring 'no bgp no-rib' on router r1")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "no bgp no-rib"')
+
+ # Checking BGP config - should show the "bgp no-rib" under the router bgp section
+ logger.info("Checking BGP configuration for 'bgp no-rib'")
+
+ norib_cfg = (
+ tgen.net["r1"]
+ .cmd('vtysh -c "show running bgpd" | grep "^ bgp no-rib"')
+ .rstrip()
+ )
+
+ assertmsg = (
+ "'no bgp no-rib'configuration applied, but still visible in configuration"
+ )
+ assert norib_cfg == "", assertmsg
+
+
+def test_bgp_disable_norib_routes():
+ "Test Routes in Zebra and BGP with the 'bgp-norib' configuration"
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Checking local BGP routes - they need to be gone from Zebra
+ logger.info("Checking Zebra routes after removing bgp shutdown on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ip_route.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "Zebra IPv4 Routes wrong after removing the 'bgp no-rib'"
+ assert res is None, assertmsg
+
+ # Check BGP Summary on local and remote routers
+ for rtrNum in [1, 2, 4]:
+ logger.info(
+ "Checking BGP Summary after removing the 'bgp no-rib' on router r1 on router r{}".format(
+ rtrNum
+ )
+ )
+
+ router = tgen.gears["r{}".format(rtrNum)]
+ reffile = os.path.join(CWD, "r{}/bgp_summary.json".format(rtrNum))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "BGP sessions on router R{} has incorrect routes after removing 'bgp no-rib on r1'".format(
+ rtrNum
+ )
+ assert res is None, assertmsg
+
+ # tgen.mininet_cli()
+
+
+def test_bgp_delayopen_without():
+ "Optional test of BGP functionality and behaviour without DelayOpenTimer enabled to establish a reference for following tests"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # part 1: no delay r1 <=> no delay r4
+ logger.info(
+ "Starting optional test of BGP functionality without DelayOpenTimer enabled to establish a reference for following tests"
+ )
+
+ # 1.1 enable peering shutdown
+ logger.info("Enable shutdown of peering between r1 and r4")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"'
+ )
+
+ # 1.2 wait for peers to shut down (poll output)
+ for router_num in [1, 4]:
+ logger.info(
+ "Checking BGP summary after enabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
+ assert res is None, assertmsg
+
+ # 1.3 disable peering shutdown
+ logger.info("Disable shutdown of peering between r1 and r4")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"'
+ )
+
+ # 1.4 wait for peers to establish connection (poll output)
+ for router_num in [1, 4]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assertmsg = (
+ "BGP session on r{} did not establish a connection with peer".format(
+ router_num
+ )
+ )
+ assert res is None, assertmsg
+
+ # tgen.mininet_cli()
+
+ # end test_bgp_delayopen_without
+
+
+def test_bgp_delayopen_singular():
+ "Test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering"
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # part 2: delay 240s r1 <=> no delay r4
+ logger.info(
+ "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on one side of the peering"
+ )
+
+ # 2.1 enable peering shutdown
+ logger.info("Enable shutdown of peering between r1 and r4")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 shutdown"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65100" -c "neighbor 192.168.101.1 shutdown"'
+ )
+
+ # 2.2 wait for peers to shut down (poll output)
+ for router_num in [1, 4]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
+ assert res is None, assertmsg
+
+ # 2.3 set delayopen on R1 to 240
+ logger.info("Setting DelayOpenTime for neighbor r4 to 240 seconds on r1")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.101.2 timers delayopen 240"'
+ )
+
+ # 2.4 check config (poll output)
+ logger.info("Checking BGP neighbor configuration after setting DelayOpenTime on r1")
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/bgp_delayopen_neighbor.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show bgp neighbors json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r1 failed to set DelayOpenTime for r4"
+ assert res is None, assertmsg
+
+ # 2.5 disable peering shutdown
+ logger.info("Disable shutdown of peering between r1 and r4")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 shutdown"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65100" -c "no neighbor 192.168.101.1 shutdown"'
+ )
+
+ # 2.6 wait for peers to establish connection (poll output)
+ for router_num in [1, 4]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assertmsg = (
+ "BGP session on r{} did not establish a connection with peer".format(
+ router_num
+ )
+ )
+ assert res is None, assertmsg
+
+ # 2.7 unset delayopen on R1
+ logger.info("Disabling DelayOpenTimer for neighbor r4 on r1")
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.101.2 timers delayopen"'
+ )
+
+ # 2.8 check config (poll output)
+ logger.info(
+ "Checking BGP neighbor configuration after disabling DelayOpenTimer on r1"
+ )
+ delayopen_cfg = (
+ tgen.net["r1"]
+ .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"')
+ .rstrip()
+ )
+ assertmsg = "BGP session on r1 failed disable DelayOpenTimer for peer r4"
+ assert delayopen_cfg == "", assertmsg
+
+ # tgen.mininet_cli()
+
+ # end test_bgp_delayopen_singular
+
+
+def test_bgp_delayopen_dual():
+ "Test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # part 3: delay 60s R2 <=> delay 30s R5
+ logger.info(
+ "Starting test of BGP functionality and behaviour with DelayOpenTimer enabled on both sides of the peering with different timer intervals"
+ )
+
+ # 3.1 enable peering shutdown
+ logger.info("Enable shutdown of peering between r2 and r5")
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 shutdown"'
+ )
+ tgen.net["r5"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 shutdown"'
+ )
+
+ # 3.2 wait for peers to shut down (pool output)
+ for router_num in [2, 5]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_shutdown.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r{} did not shut down peer".format(router_num)
+ assert res is None, assertmsg
+
+ # 3.3 set delayopen on R2 to 60s and on R5 to 30s
+ logger.info("Setting DelayOpenTime for neighbor r5 to 60 seconds on r2")
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "neighbor 192.168.201.2 timers delayopen 60"'
+ )
+ logger.info("Setting DelayOpenTime for neighbor r2 to 30 seconds on r5")
+ tgen.net["r5"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65200" -c "neighbor 192.168.201.1 timers delayopen 30"'
+ )
+
+ # 3.4 check config (poll output)
+ for router_num in [2, 5]:
+ logger.info(
+ "Checking BGP neighbor configuration after setting DelayOpenTime on r{}i".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_neighbor.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show bgp neighbors json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r{} failed to set DelayOpenTime".format(router_num)
+ assert res is None, assertmsg
+
+ ## 3.5 disable peering shutdown
+ logger.info("Disable shutdown of peering between r2 and r5")
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 shutdown"'
+ )
+ tgen.net["r5"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 shutdown"'
+ )
+
+ ## 3.6 wait for peers to reach connect or active state (poll output)
+ delay_start = int(time.time())
+ for router_num in [2, 5]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_connect.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=3, wait=1)
+ assertmsg = "BGP session on r{} did not enter Connect state with peer".format(
+ router_num
+ )
+ assert res is None, assertmsg
+
+ ## 3.7 wait for peers to establish connection (poll output)
+ for router_num in [2, 5]:
+ logger.info(
+ "Checking BGP summary after disabling shutdown of peering on r{}".format(
+ router_num
+ )
+ )
+ router = tgen.gears["r{}".format(router_num)]
+ reffile = os.path.join(
+ CWD, "r{}/bgp_delayopen_summary_established.json".format(router_num)
+ )
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=35, wait=1)
+ assertmsg = (
+ "BGP session on r{} did not establish a connection with peer".format(
+ router_num
+ )
+ )
+ assert res is None, assertmsg
+
+ delay_stop = int(time.time())
+ assertmsg = "BGP peering between r2 and r5 was established before DelayOpenTimer (30sec) on r2 could expire"
+ assert (delay_stop - delay_start) > 30, assertmsg
+
+ # 3.8 unset delayopen on R2 and R5
+ logger.info("Disabling DelayOpenTimer for neighbor r5 on r2")
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65000" -c "no neighbor 192.168.201.2 timers delayopen"'
+ )
+ logger.info("Disabling DelayOpenTimer for neighbor r2 on r5")
+ tgen.net["r5"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65200" -c "no neighbor 192.168.201.1 timers delayopen"'
+ )
+
+ # 3.9 check config (poll output)
+ for router_num in [2, 5]:
+ logger.info(
+ "Checking BGP neighbor configuration after disabling DelayOpenTimer on r{}".format(
+ router_num
+ )
+ )
+ delayopen_cfg = (
+ tgen.net["r{}".format(router_num)]
+ .cmd('vtysh -c "show bgp neighbors json" | grep "DelayOpenTimeMsecs"')
+ .rstrip()
+ )
+ assertmsg = "BGP session on r{} failed disable DelayOpenTimer".format(
+ router_num
+ )
+ assert delayopen_cfg == "", assertmsg
+
+ # tgen.mininet_cli()
+
+ # end test_bgp_delayopen_dual
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_flowspec/__init__.py b/tests/topotests/bgp_flowspec/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/__init__.py
diff --git a/tests/topotests/bgp_flowspec/exabgp.env b/tests/topotests/bgp_flowspec/exabgp.env
new file mode 100644
index 0000000..a328e04
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/exabgp.env
@@ -0,0 +1,54 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_flowspec/peer1/exabgp.cfg b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg
new file mode 100644
index 0000000..cd1fae5
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/peer1/exabgp.cfg
@@ -0,0 +1,32 @@
+neighbor 10.0.1.1 {
+router-id 10.0.1.101;
+local-address 10.0.1.101;
+local-as 100;
+peer-as 100;
+flow {
+route {
+match {
+source 1.1.1.2/32;
+destination 3.3.3.3/32;
+packet-length <200;
+}
+then {
+redirect 50.0.0.2;
+rate-limit 55;
+}
+}
+#end route 1
+route {
+match {
+source 1::2/128/0;
+destination 3::3/128/0;
+packet-length <200;
+}
+then {
+redirect 50::2;
+rate-limit 55;
+}
+}
+#end route 2
+}
+}
diff --git a/tests/topotests/bgp_flowspec/r1/bgpd.conf b/tests/topotests/bgp_flowspec/r1/bgpd.conf
new file mode 100644
index 0000000..4b7a20f
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/r1/bgpd.conf
@@ -0,0 +1,19 @@
+!
+hostname r1
+password zebra
+log stdout debugging
+router bgp 100
+ bgp router-id 10.0.1.1
+ neighbor 10.0.1.101 remote-as 100
+ neighbor 10.0.1.101 timers 3 10
+ neighbor 10.0.1.101 update-source 10.0.1.1
+ address-family ipv6 flowspec
+ local-install r1-eth0
+ neighbor 10.0.1.101 activate
+ exit-address-family
+ address-family ipv4 flowspec
+ local-install r1-eth0
+ neighbor 10.0.1.101 activate
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_flowspec/r1/summary.txt b/tests/topotests/bgp_flowspec/r1/summary.txt
new file mode 100644
index 0000000..82426f3
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/r1/summary.txt
@@ -0,0 +1,50 @@
+{
+"ipv4Unicast":{
+ "routerId":"10.0.1.1",
+ "as":100,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":0,
+ "pfxSnt":0,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+},
+"ipv4Flowspec":{
+ "routerId":"10.0.1.1",
+ "as":100,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":1,
+ "pfxSnt":0,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+},
+"ipv6Flowspec":{
+ "routerId":"10.0.1.1",
+ "as":100,
+ "vrfName":"default",
+ "peerCount":1,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":1,
+ "pfxSnt":0,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+}
+}
diff --git a/tests/topotests/bgp_flowspec/r1/zebra.conf b/tests/topotests/bgp_flowspec/r1/zebra.conf
new file mode 100644
index 0000000..4b103cb
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+hostname r1
+password zebra
+ip table range 500 600
+interface r1-eth0
+ ip address 10.0.1.1/24
+ ipv6 address 1001::1/112
+!
+
diff --git a/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py
new file mode 100644
index 0000000..682ff4c
--- /dev/null
+++ b/tests/topotests/bgp_flowspec/test_bgp_flowspec_topo.py
@@ -0,0 +1,203 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_flowspec_topo.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_flowspec_topo.py: Test BGP topology with Flowspec EBGP peering
+
+
+ +------+------+
+ | peer1 |
+ | BGP peer 1 |
+ |192.168.0.161|
+ | |
+ +------+------+
+ .2 | r1-eth0
+ |
+ ~~~~~~~~~
+ +---~~ s1 ~~------+
+ ~~ ~~
+ ~~~~~~~~~
+ | 10.0.1.1 r1-eth0
+ | 1001::1 r1-eth0
+ +--------+--------+
+ | r1 |
+ |BGP 192.168.0.162|
+ | |
+ | |
+ | |
+ +-----------------+
+
+"""
+
+import json
+import functools
+import os
+import sys
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+ # Setup Control Path Switch 1. r1-eth0
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ ## Add eBGP ExaBGP neighbors
+ peer_ip = "10.0.1.101" ## peer
+ peer_route = "via 10.0.1.1" ## router
+ peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route)
+ switch.add_link(peer)
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+
+ tgen.start_topology()
+ # check for zebra capability
+ router = tgen.gears["r1"]
+
+ # Get r1 reference and run Daemons
+ logger.info("Launching BGP and ZEBRA on r1")
+ router = tgen.gears["r1"]
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1"))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))
+ )
+ router.start()
+
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ peer_dir = os.path.join(CWD, pname)
+ env_file = os.path.join(CWD, "exabgp.env")
+ peer.start(peer_dir, env_file)
+ logger.info(pname)
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ "Test for BGP topology convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp convergence")
+
+ # Expected result
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/summary.txt")
+
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show bgp summary json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5)
+ assertmsg = "BGP router network did not converge"
+ assert res is None, assertmsg
+
+
+def test_bgp_flowspec():
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP")
+ output = router.vtysh_cmd(
+ "show bgp ipv4 flowspec 3.3.3.3", isjson=False, daemon="bgpd"
+ )
+ logger.info(output)
+ if (
+ "NH 50.0.0.2" not in output
+ or "FS:redirect IP" not in output
+ or "Packet Length < 200" not in output
+ ):
+ assertmsg = "traffic to 3.3.3.3 should have been detected as FS entry. NOK"
+ assert 0, assertmsg
+ else:
+ logger.info("Check BGP FS entry for 3.3.3.3 with redirect IP OK")
+
+ logger.info("Check BGP FS entry for 3::3 with redirect IP")
+ output = router.vtysh_cmd(
+ "show bgp ipv6 flowspec 3::3", isjson=False, daemon="bgpd"
+ )
+ logger.info(output)
+ if (
+ "NH 50::2" not in output
+ or "FS:redirect IP" not in output
+ or "Packet Length < 200" not in output
+ ):
+ assertmsg = "traffic to 3::3 should have been detected as FS entry. NOK"
+ assert 0, assertmsg
+ else:
+ logger.info("Check BGP FS entry for 3::3 with redirect IP OK")
+
+
+if __name__ == "__main__":
+
+ args = ["-s"] + sys.argv[1:]
+ ret = pytest.main(args)
+
+ sys.exit(ret)
diff --git a/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json
new file mode 100644
index 0000000..7b3cac8
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/bgp_gr_topojson_topo1.json
@@ -0,0 +1,115 @@
+{
+ "ipv4base":"192.168.0.0",
+ "ipv4mask":24,
+ "ipv6base":"fd00::",
+ "ipv6mask":64,
+ "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64},
+ "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128},
+ "routers":{
+ "r1":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2-link1": {"ipv4":"auto", "ipv6":"auto"},
+ "r2-link2": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"100.0.10.1/32",
+ "no_of_ip":5,
+ "next_hop":"192.168.1.2"
+ },
+ {
+ "network":"1::1/128",
+ "no_of_ip":5,
+ "next_hop":"fd00:0:0:1::2"
+ }]},
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4":"auto", "ipv6":"auto"},
+ "r1-link2": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"200.0.20.1/32",
+ "no_of_ip":5,
+ "next_hop":"192.168.1.1"
+ },
+ {
+ "network":"2::1/128",
+ "no_of_ip":5,
+ "next_hop":"fd00:0:0:1::1"
+ }]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py
new file mode 100644
index 0000000..f155325
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-1.py
@@ -0,0 +1,1542 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 2 routers topology, r1, r2 in IBGP
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Garceful Restart on both the routers.
+
+1. Transition from Peer-level helper to Global Restarting
+2. Transition from Peer-level helper to Global inherit helper
+3. Transition from Peer-level restarting to Global inherit helper
+4. Default GR functional mode is Helper.
+5. Verify that the restarting node sets "R" bit while sending the
+ BGP open messages after the node restart, only if GR is enabled.
+6. Verify if restarting node resets R bit in BGP open message
+ during normal BGP session flaps as well, even when GR restarting
+ mode is enabled. Here link flap happen due to interface UP/DOWN.
+7. Verify if restarting node resets R bit in BGP
+ open message during normal BGP session flaps when GR is disabled.
+8. Verify that restarting nodes set "F" bit while sending
+ the BGP open messages after it restarts, only when BGP GR is enabled.
+9. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+10. Verify that GR helper routers keeps all the routes received
+ from restarting node if both the routers are configured as
+ GR restarting node.
+11. Verify that GR helper routers delete all the routes
+ received from a node if both the routers are configured as GR
+ helper node.
+12. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+13. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+14. Verify that restarting nodes reset "F" bit while sending
+ the BGP open messages after it's restarts, when BGP GR is **NOT** enabled.
+15. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+16. Transition from Global Restarting to Disable and then Global
+ Disable to Restarting.
+17. Transition from Global Helper to Disable and then Global
+ Disable to Helper.
+18. Transition from Global Restart to Helper and then Global
+ Helper to Restart, Global Mode : GR Restarting
+ PerPeer Mode : GR Helper
+ GR Mode effective : GR Helper
+19. Transition from Peer-level helper to Global Restarting,
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+20. Transition from Peer-level restart to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+21. Transition from Peer-level disabled to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Disabled
+ GR Mode effective : GR Disabled
+22. Peer-level inherit from Global Restarting
+ Global Mode : GR Restart
+ PerPeer Mode : None
+ GR Mode effective : GR Restart
+23. Transition from Peer-level disable to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Disable
+ GR Mode effective : GR Disable
+
+These tests have been broken up into 4 sub python scripts because
+the totality of this run was fairly significant.
+"""
+
+import os
+import sys
+import time
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ shutdown_bringup_interface,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"}
+NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"}
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 20
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def BGP_GR_TC_50_p1(request):
+ """
+ Test Objective : Transition from Peer-level helper to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : Helper
+ GR Mode effective : GR Helper
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 as GR helper node at per Peer-level for R2"
+ " and configure R2 as global restarting node."
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGP on R2 and remove Peer-level GR config from R1 ")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R2 that R1 still advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Start BGP on R2")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_51_p1(request):
+ """
+ Test Objective : Transition from Peer-level restarting to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Restart
+ GR Mode effective : GR Restart
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure R1 as GR restarting node at per Peer-level for R2")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB & R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, "r2", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGP on R1 and remove Peer-level GR config")
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Start BGP on R2")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_53_p1(request):
+ """
+ Test Objective : Default GR functional mode is Helper.
+ Global Mode : None
+ PerPeer Mode : None
+ GR Mode effective : GR Helper
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("configure R2 as global restarting node")
+
+ input_dict = {"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}}
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step(
+ "Verify on R2 that R1 advertises GR capabilities as a helper node based on inherit"
+ )
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGPd on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Start BGP on R2")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_4_p0(request):
+ """
+ Test Objective : Verify that the restarting node sets "R" bit while sending the
+ BGP open messages after the node restart, only if GR is enabled.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Helper Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : R2 goes for reload ")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R2 is still down, restart time {} sec."
+ "So time verify the routes are present in BGP RIB and ZEBRA ".format(
+ GR_RESTART_TIMER
+ )
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Phase 5] : R2 is about to come up now ")
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_5_1_2_p1(request):
+ """
+ Test Objective : Verify if restarting node resets R bit in BGP open message
+ during normal BGP session flaps as well, even when GR restarting mode is enabled.
+ Here link flap happen due to interface UP/DOWN.
+
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : Now flap the link running the BGP session ")
+ # Shutdown interface
+ intf = "r2-r1-eth0"
+ shutdown_bringup_interface(tgen, "r2", intf)
+
+ # Bring up Interface
+ shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True)
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : Restart BGPd on router R2. ")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_6_1_2_p1(request):
+ """
+ Test Objective : Verify if restarting node resets R bit in BGP
+ open message during normal BGP session flaps when GR is disabled.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" "[Restart Mode]R1-----R2[Helper Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 1] : Changing mode" "[Disable Mode]R1-----R2[Helper Mode]")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verify GR stats
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ # here the verify_graceful_restart fro the neighbor would be
+ # "NotReceived" as the latest GR config is not yet applied.
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : Now flap the link running the BGP session ")
+ # Shutdown interface
+ intf = "r2-r1-eth0"
+ shutdown_bringup_interface(tgen, "r2", intf)
+
+ # Bring up Interface
+ shutdown_bringup_interface(tgen, "r2", intf, ifaceaction=True)
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r2: R-bit is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Restart BGPd on R2 ")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r2: R-bit is set to True\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/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py
new file mode 100644
index 0000000..dda3bd4
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-2.py
@@ -0,0 +1,404 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 2 routers topology, r1, r2 in IBGP
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Garceful Restart on both the routers.
+
+1. Transition from Peer-level helper to Global Restarting
+2. Transition from Peer-level helper to Global inherit helper
+3. Transition from Peer-level restarting to Global inherit helper
+4. Default GR functional mode is Helper.
+5. Verify that the restarting node sets "R" bit while sending the
+ BGP open messages after the node restart, only if GR is enabled.
+6. Verify if restarting node resets R bit in BGP open message
+ during normal BGP session flaps as well, even when GR restarting
+ mode is enabled. Here link flap happen due to interface UP/DOWN.
+7. Verify if restarting node resets R bit in BGP
+ open message during normal BGP session flaps when GR is disabled.
+8. Verify that restarting nodes set "F" bit while sending
+ the BGP open messages after it restarts, only when BGP GR is enabled.
+9. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+10. Verify that GR helper routers keeps all the routes received
+ from restarting node if both the routers are configured as
+ GR restarting node.
+11. Verify that GR helper routers delete all the routes
+ received from a node if both the routers are configured as GR
+ helper node.
+12. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+13. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+14. Verify that restarting nodes reset "F" bit while sending
+ the BGP open messages after it's restarts, when BGP GR is **NOT** enabled.
+15. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+16. Transition from Global Restarting to Disable and then Global
+ Disable to Restarting.
+17. Transition from Global Helper to Disable and then Global
+ Disable to Helper.
+18. Transition from Global Restart to Helper and then Global
+ Helper to Restart, Global Mode : GR Restarting
+ PerPeer Mode : GR Helper
+ GR Mode effective : GR Helper
+19. Transition from Peer-level helper to Global Restarting,
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+20. Transition from Peer-level restart to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+21. Transition from Peer-level disabled to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Disabled
+ GR Mode effective : GR Disabled
+22. Peer-level inherit from Global Restarting
+ Global Mode : GR Restart
+ PerPeer Mode : None
+ GR Mode effective : GR Restart
+23. Transition from Peer-level disable to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Disable
+ GR Mode effective : GR Disable
+
+These tests have been broken up into 4 sub python scripts because
+the totality of this run was fairly significant.
+"""
+
+import os
+import sys
+import time
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ shutdown_bringup_interface,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"}
+NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"}
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 20
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def test_BGP_GR_TC_8_p1(request):
+ """
+ Test Objective : Verify that restarting nodes set "F" bit while sending
+ the BGP open messages after it restarts, only when BGP GR is enabled.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Restart Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : R1 goes for reload ")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Phase 3] : R1 is about to come up now ")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_f_bit(tgen, topo, addr_type, input_dict, dut="r2", peer="r1")
+ 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/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py
new file mode 100644
index 0000000..e4b533b
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-3.py
@@ -0,0 +1,2753 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 2 routers topology, r1, r2 in IBGP
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Garceful Restart on both the routers.
+
+1. Transition from Peer-level helper to Global Restarting
+2. Transition from Peer-level helper to Global inherit helper
+3. Transition from Peer-level restarting to Global inherit helper
+4. Default GR functional mode is Helper.
+5. Verify that the restarting node sets "R" bit while sending the
+ BGP open messages after the node restart, only if GR is enabled.
+6. Verify if restarting node resets R bit in BGP open message
+ during normal BGP session flaps as well, even when GR restarting
+ mode is enabled. Here link flap happen due to interface UP/DOWN.
+7. Verify if restarting node resets R bit in BGP
+ open message during normal BGP session flaps when GR is disabled.
+8. Verify that restarting nodes set "F" bit while sending
+ the BGP open messages after it restarts, only when BGP GR is enabled.
+9. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+10. Verify that GR helper routers keeps all the routes received
+ from restarting node if both the routers are configured as
+ GR restarting node.
+11. Verify that GR helper routers delete all the routes
+ received from a node if both the routers are configured as GR
+ helper node.
+12. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+13. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+14. Verify that restarting nodes reset "F" bit while sending
+ the BGP open messages after it's restarts, when BGP GR is **NOT** enabled.
+15. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+16. Transition from Global Restarting to Disable and then Global
+ Disable to Restarting.
+17. Transition from Global Helper to Disable and then Global
+ Disable to Helper.
+18. Transition from Global Restart to Helper and then Global
+ Helper to Restart, Global Mode : GR Restarting
+ PerPeer Mode : GR Helper
+ GR Mode effective : GR Helper
+19. Transition from Peer-level helper to Global Restarting,
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+20. Transition from Peer-level restart to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+21. Transition from Peer-level disabled to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Disabled
+ GR Mode effective : GR Disabled
+22. Peer-level inherit from Global Restarting
+ Global Mode : GR Restart
+ PerPeer Mode : None
+ GR Mode effective : GR Restart
+23. Transition from Peer-level disable to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Disable
+ GR Mode effective : GR Disable
+
+These tests have been broken up into 4 sub python scripts because
+the totality of this run was fairly significant.
+"""
+
+import os
+import sys
+import time
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ shutdown_bringup_interface,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"}
+NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"}
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 20
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def BGP_GR_TC_50_p1(request):
+ """
+ Test Objective : Transition from Peer-level helper to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : Helper
+ GR Mode effective : GR Helper
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 as GR helper node at per Peer-level for R2"
+ " and configure R2 as global restarting node."
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGP on R2 and remove Peer-level GR config from R1 ")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R2 that R1 still advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Start BGP on R2")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_19_p1(request):
+ """
+ Test Objective : Verify that GR helper routers keeps all the routes received
+ from restarting node if both the routers are configured as GR restarting node.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Restart] initialized ")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "[Phase 2] : R1's Gr state cahnge to Graceful"
+ " Restart without resetting the session "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ logger.info(
+ "[Phase 3] : R2 is still down, restart time 120 sec."
+ " So time verify the routes are present in BGP RIB and ZEBRA "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_20_p1(request):
+ """
+ Test Objective : Verify that GR helper routers delete all the routes
+ received from a node if both the routers are configured as GR helper node.
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info("[Phase 1] : Test Setup [Helper]R1-----R2[Helper] initialized ")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Phase 5] : R2 is about to come up now ")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_31_1_p1(request):
+ """
+ After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" " [Helper Mode]R2-----R1[Restart Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : R1 Goes from Restart to Disable Mode ")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verify GR stats
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : R1 goes for reload ")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R1 is still down, restart time 120 sec."
+ " So time verify the routes are not present in BGP RIB and ZEBRA"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+
+ logger.info("[Phase 4] : R1 is about to come up now ")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Phase 5] : R1 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_31_2_p1(request):
+ """
+ After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup " "[Disable Mode]R1-----R2[Restart Mode] initialized "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 2] : R2 Goes from Disable to Restart Mode ")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verify GR stats
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ # here the verify_graceful_restart fro the neighbor would be
+ # "NotReceived" as the latest GR config is not yet applied.
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 6] : R1 is about to come up now ")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 3] : R1 goes for reload ")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info(
+ "[Phase 4] : R1 is still down, restart time 120 sec."
+ " So time verify the routes are present in BGP RIB and ZEBRA "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Phase 6] : R1 is about to come up now ")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Phase 4] : R1 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_9_p1(request):
+ """
+ Test Objective : Verify that restarting nodes reset "F" bit while sending
+ the BGP open messages after it's restarts, when BGP GR is **NOT** enabled.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Phase 1] : Test Setup" " [Restart Mode]R1-----R2[Helper Mode] Initiliazed "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Phase 2] : R2 goes for reload ")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R2 is still down, restart time 120 sec."
+ "So time verify the routes are present in BGP RIB and ZEBRA "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Phase 5] : R2 is about to come up now ")
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert (
+ result is True
+ ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_f_bit(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: F-bit is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_17_p1(request):
+ """
+ Test Objective : Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info("[Phase 1] : Test Setup [Disable]R1-----R2[Restart] " "Initiliazed ")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Phase 2] : R2 goes for reload ")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info(
+ "[Phase 3] : R2 is still down, restart time 120 sec."
+ " So time verify the routes are present in BGP RIB and ZEBRA "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Phase 5] : R2 is about to come up now ")
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("[Phase 4] : R2 is UP now, so time to collect GR stats ")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert (
+ result is True
+ ), "BGP Convergence after BGPd restart" " :Failed \n Error:{}".format(result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_r_bit(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: R-bit is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_43_p1(request):
+ """
+ Test Objective : Transition from Global Restarting to Disable
+ and then Global Disable to Restarting.
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure R1 and R2 as GR restarting node in global level")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps BGP routes in zebra and R2 retains"
+ " the stale entry for received routes from R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Bring up BGPd on R1 and configure it as GR disabled node in global level")
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": False,
+ "graceful-restart-disable": True,
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 doesn't advertise any GR capabilities")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 flush all BGP routes from RIB & FIB and FIB and R2"
+ " does not retain stale entry for received routes from R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Bring up BGPd on R1 and configure it as GR" " restarting node in global level"
+ )
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {"r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}}}
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps BGP routes in zebra and R2"
+ " retains the stale entry for received routes from R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_44_p1(request):
+ """
+ Test Objective : Transition from Global Helper to Disable
+ and then Global Disable to Helper.
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R2 as GR restating node in global level and"
+ " leave R1 without any GR related config"
+ )
+
+ input_dict = {"r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}}}
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Bring up BGPd on R2 and configure R1 as GR disabled node in global level")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 doesn't advertise any GR capabilities")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step("Verify that R1 does not retain stale entry for received routes from R2")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ next_hop = NEXT_HOP_IP_2[addr_type]
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGPd on R2 and remove GR related config from R1 in global level")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-disable": False}}}
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_45_p1(request):
+ """
+ Test Objective : Transition from Global Restart to Helper
+ and then Global Helper to Restart.
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure R1 and R2 as GR restarting node in global level")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps BGP routes in zebra and R2"
+ " retains the stale entry for received routes from R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Bring up BGPd on R1 and remove GR related config in global level")
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": False,
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-helper": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step("Verify that R1 keeps stale entry for BGP routes when BGPd on R2 is down")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGPd on R2 and configure R1 as GR restarting node in global level")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps BGP routes in zebra and R2"
+ " retains the stale entry for received routes from R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ 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/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py
new file mode 100644
index 0000000..835ef41
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo1/test_bgp_gr_functionality_topo1-4.py
@@ -0,0 +1,1686 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 2 routers topology, r1, r2 in IBGP
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Garceful Restart on both the routers.
+
+1. Transition from Peer-level helper to Global Restarting
+2. Transition from Peer-level helper to Global inherit helper
+3. Transition from Peer-level restarting to Global inherit helper
+4. Default GR functional mode is Helper.
+5. Verify that the restarting node sets "R" bit while sending the
+ BGP open messages after the node restart, only if GR is enabled.
+6. Verify if restarting node resets R bit in BGP open message
+ during normal BGP session flaps as well, even when GR restarting
+ mode is enabled. Here link flap happen due to interface UP/DOWN.
+7. Verify if restarting node resets R bit in BGP
+ open message during normal BGP session flaps when GR is disabled.
+8. Verify that restarting nodes set "F" bit while sending
+ the BGP open messages after it restarts, only when BGP GR is enabled.
+9. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+10. Verify that GR helper routers keeps all the routes received
+ from restarting node if both the routers are configured as
+ GR restarting node.
+11. Verify that GR helper routers delete all the routes
+ received from a node if both the routers are configured as GR
+ helper node.
+12. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+13. After BGP neighborship is established and GR capability is exchanged,
+ transition restarting router to disabled state and vice versa.
+14. Verify that restarting nodes reset "F" bit while sending
+ the BGP open messages after it's restarts, when BGP GR is **NOT** enabled.
+15. Verify that only GR helper routers keep the stale
+ route entries, not any GR disabled router.
+16. Transition from Global Restarting to Disable and then Global
+ Disable to Restarting.
+17. Transition from Global Helper to Disable and then Global
+ Disable to Helper.
+18. Transition from Global Restart to Helper and then Global
+ Helper to Restart, Global Mode : GR Restarting
+ PerPeer Mode : GR Helper
+ GR Mode effective : GR Helper
+19. Transition from Peer-level helper to Global Restarting,
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+20. Transition from Peer-level restart to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+21. Transition from Peer-level disabled to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Disabled
+ GR Mode effective : GR Disabled
+22. Peer-level inherit from Global Restarting
+ Global Mode : GR Restart
+ PerPeer Mode : None
+ GR Mode effective : GR Restart
+23. Transition from Peer-level disable to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Disable
+ GR Mode effective : GR Disable
+
+These tests have been broken up into 4 sub python scripts because
+the totality of this run was fairly significant.
+"""
+
+import os
+import sys
+import time
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ shutdown_bringup_interface,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+NEXT_HOP_IP = {"ipv4": "192.168.1.10", "ipv6": "fd00:0:0:1::10"}
+NEXT_HOP_IP_1 = {"ipv4": "192.168.0.1", "ipv6": "fd00::1"}
+NEXT_HOP_IP_2 = {"ipv4": "192.168.0.2", "ipv6": "fd00::2"}
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 20
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"]["r2-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def BGP_GR_TC_50_p1(request):
+ """
+ Test Objective : Transition from Peer-level helper to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : Helper
+ GR Mode effective : GR Helper
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 as GR helper node at per Peer-level for R2"
+ " and configure R2 as global restarting node."
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a helper node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bring up BGP on R2 and remove Peer-level GR config from R1 ")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, "r1", neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R2 that R1 still advertises GR capabilities as a helper node")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, "r2", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, "r2", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ next_hop = next_hop_per_address_family(
+ tgen, "r1", "r2", addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_topo, next_hop)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Start BGP on R2")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_46_p1(request):
+ """
+ Test Objective : transition from Peer-level helper to Global Restarting
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Helper
+ GR Mode effective : GR Helper
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 and R2 as GR restarting node in global"
+ " and helper in per-Peer-level"
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in RIB & FIB and R2 keeps stale entries in FIB using"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Bring up BGP on R1 and remove Peer-level GR config"
+ " from R1 following by a session reset"
+ )
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-helper": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB command and R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_47_p1(request):
+ """
+ Test Objective : transition from Peer-level restart to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Restarting
+ GR Mode effective : GR Restarting
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure R1 and R2 as GR restarting node in global and per-Peer-level")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Bring up BGP on R1 and remove Peer-level GR"
+ " config from R1 following by a session reset"
+ )
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 still advertises GR capabilities as a restarting node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_48_p1(request):
+ """
+ Test Objective : transition from Peer-level disabled to Global Restart
+ Global Mode : GR Restarting
+ PerPeer Mode : GR Disabled
+ GR Mode effective : GR Disabled
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 as GR restarting node in global level and"
+ " GR Disabled in per-Peer-level"
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 does't advertise any GR capabilities")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step("Verify on R2 and R1 that none of the routers keep stale entries")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+
+ step("Bring up BGP on R1 and remove Peer-level GR config from R1")
+
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 advertises GR capabilities as a restarting node")
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_49_p1(request):
+ """
+ Test Objective : Peer-level inherit from Global Restarting
+ Global Mode : GR Restart
+ PerPeer Mode : None
+ GR Mode effective : GR Restart
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Configure R1 as GR restarting node in global level")
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step(
+ "Verify that R2 receives GR restarting capabilities"
+ " from R1 based on inheritence"
+ )
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGPd on router R1")
+
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ step(
+ "Verify that R1 keeps the stale entries in FIB and R2 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ peer = "r2"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r2"
+ peer = "r1"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def BGP_GR_TC_52_p1(request):
+ """
+ Test Objective : Transition from Peer-level disable to Global inherit helper
+ Global Mode : None
+ PerPeer Mode : GR Disable
+ GR Mode effective : GR Disable
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure R1 as GR disabled node at per Peer-level for R2"
+ " & R2 as GR restarting node"
+ )
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step("Verify on R2 that R1 does't advertise any GR capabilities")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 doesn't keep RIB & FIB entries."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop, protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+
+ step("Bring up BGP on R2 and remove Peer-level GR config from R1")
+
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"graceful-restart-disable": False}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ step(
+ "Verify on R2 that R1 advertises GR capabilities as a helper node from global inherit"
+ )
+
+ input_dict = {
+ "r1": {"bgp": {"graceful-restart": {"graceful-restart-helper": True}}},
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Kill BGP on R2")
+
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ step(
+ "Verify that R2 keeps the stale entries in FIB & R1 keeps stale entries in RIB & FIB"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ peer = "r1"
+ protocol = "bgp"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_1
+ )
+ input_topo = {"r1": topo["routers"]["r1"]}
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ peer = "r2"
+ next_hop = next_hop_per_address_family(
+ tgen, dut, peer, addr_type, NEXT_HOP_IP_2
+ )
+ input_topo = {"r2": topo["routers"]["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop, protocol)
+ assert (
+ result is True
+ ), "Testcase {} :Failed \n Routes are still present \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/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json
new file mode 100644
index 0000000..75f192d
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/bgp_gr_topojson_topo2.json
@@ -0,0 +1,334 @@
+{
+ "ipv4base":"192.168.0.0",
+ "ipv4mask":24,
+ "ipv6base":"fd00::",
+ "ipv6mask":64,
+ "link_ip_start":{"ipv4":"192.168.0.0", "v4mask":24, "ipv6":"fd00::", "v6mask":64},
+ "lo_prefix":{"ipv4":"1.0.", "v4mask":32, "ipv6":"2001:DB8:F::", "v6mask":128},
+ "routers":{
+ "r1":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4":"auto", "ipv6":"auto"},
+ "r3": {"ipv4":"auto", "ipv6":"auto"},
+ "r5": {"ipv4":"auto", "ipv6":"auto"},
+ "r6": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "101.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "1::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "102.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "2::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4":"auto", "ipv6":"auto"},
+ "r4": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "103.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "3::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "104.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "4::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as": "500",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "105.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "5::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r6":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as": "600",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "106.0.20.1/32",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "6::1/128",
+ "no_of_network": 5
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py
new file mode 100644
index 0000000..3afe388
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-1.py
@@ -0,0 +1,1515 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 7 routers topology
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Graceful Restart on both the routers.
+
+TC_1_2:
+ Verify that EOR message is sent out only after initial convergence
+ Verify whether EOR message is received from all the peers after restart
+TC_3:
+ Verify the selection deferral timer functionality when EOR is not sent
+ by the helper router
+TC_11:
+ Verify that selection-deferral timer sets the maximum time to
+ avoid deadlock during which the best-path
+TC_10:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_15:
+ Test Objective : Test GR scenarios by enabling Graceful Restart
+ for multiple address families..
+TC_16:
+ Test Objective : Verify BGP-GR feature when restarting node
+ is a transit router for it's iBGP peers.
+TC_18:
+ Test Objective : Verify that GR helper router deletes stale routes
+ received from restarting node, if GR capability is not present in
+TC_19:
+ Test Objective : Verify that GR routers keeps all the routes
+ received from restarting node if both the routers are
+TC_26:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_28:
+ Test Objective : Verify if helper node goes down before restarting
+ node comes up online, helper node sets the R-bit to avoid dead-lock
+TC_29:
+ Test Objective : Change timers on the fly, and
+ verify if it takes immediate effect.
+TC_33:
+ Test Objective : Helper router receives same prefixes from two
+ different routers (GR-restarting and GR-disabled). Keeps the
+TC_34_1:
+ Test Objective : Restarting node doesn't preserve forwarding
+ state, helper router should not keep the stale entries.
+TC_34_2:
+ Test Objective : Restarting node doesn't preserve the forwarding
+ state verify the behaviour on helper node, if it still keeps the
+TC_32:
+ Test Objective : Restarting node is connected to multiple helper
+ nodes, one of them doesn't send EOR to restarting router. Verify
+TC_37:
+ Test Objective : Verify if helper node restarts before sending the
+ EOR message, restarting node doesn't wait until stale path timer
+TC_30:
+ Test Objective : Restarting node removes stale routes from Zebra
+ after receiving an EOR from helper router.
+
+"""
+
+import os
+import sys
+import time
+import pytest
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_eor,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_gr_address_family,
+ modify_bgp_config_when_bgpd_down,
+ verify_graceful_restart_timers,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 5
+GR_SELECT_DEFER_TIMER = 5
+GR_STALEPATH_TIMER = 5
+PREFERRED_NEXT_HOP = "link_local"
+NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"]
+NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer)
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def test_BGP_GR_TC_1_2_p0(request):
+ """
+ Verify that EOR message is sent out only after initial convergence
+ Verify whether EOR message is received from all the peers after restart
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "Verify EOR Sent and Received : BGP_GR_TC_1_2 >> "
+ "BGP GR [Helper Mode]R3-----R1[Restart Mode] "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes received from router R3
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("R1 goes for reload")
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("Starting bgpd process")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+ logger.info("R1 is UP Now")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes received from router R3
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying EOR on restarting router
+ result = verify_eor(tgen, topo, addr_type, input_dict, dut="r3", peer="r1")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_3_p0(request):
+ """
+ Verify the selection deferral timer functionality when EOR is not sent
+ by the helper router
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Verify route download to RIB: BGP_GR_TC_3 >> "
+ "BGP GR [Helper Mode]R1-----R2[Restart Mode] "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "disable-eor": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ "timer": {"select-defer-time": GR_SELECT_DEFER_TIMER},
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes received from router R1
+ dut = "r2"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("R2 goes for reload ")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ logger.info("R2 is about to come up now")
+ start_router_daemons(tgen, "r2", ["bgpd"])
+ logger.info("R2 is UP Now")
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes received from router R1
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verify EOR on restarting router
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict, dut="r2", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r2: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "Waiting for selection deferral timer({} sec)..".format(GR_SELECT_DEFER_TIMER)
+ )
+ sleep(GR_SELECT_DEFER_TIMER)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, "r2", input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_TC_11_p0(request):
+ """
+ Verify that selection-deferral timer sets the maximum time to
+ avoid deadlock during which the best-path
+ selection process is deferred, after a peer session was restarted
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info("Verify EOR Sent after deferral timeout : BGP_GR_TC_11")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "select-defer-time": GR_SELECT_DEFER_TIMER,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}},
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}},
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}},
+ }
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "graceful-restart": {"disable-eor": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r1")
+ clear_bgp(tgen, addr_type, "r3")
+
+ result = verify_bgp_convergence_from_running_config(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes received from router R1
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("R1 goes for reload")
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("Starting bgpd process")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+ logger.info("R1 is UP Now")
+
+ for addr_type in ADDR_TYPES:
+ # Verify EOR on restarting router
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "Waiting for selection deferral timer({} sec).. ".format(
+ GR_SELECT_DEFER_TIMER + 2
+ )
+ )
+ sleep(GR_SELECT_DEFER_TIMER + 2)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes received from router R1
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying EOR on restarting router
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_10_p2(request):
+ """
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Test Setup: [Helper Mode]R3-----R1[Restart Mode] initialized")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "next_hop_self": True,
+ "graceful-restart": True,
+ "activate": "ipv6",
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "next_hop_self": True,
+ "graceful-restart": True,
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "graceful-restart-helper": True,
+ "activate": "ipv6",
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "graceful-restart-helper": True,
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ step(
+ "Verifying GR config and operational state for addr_type {}".format(
+ addr_type
+ )
+ )
+
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv4Unicast",
+ dut="r1",
+ peer="r3",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv6Unicast",
+ dut="r1",
+ peer="r3",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv4Unicast",
+ dut="r3",
+ peer="r1",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv6Unicast",
+ dut="r3",
+ peer="r1",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Killing bgpd on r1")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Starting bgpd on r1")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def BGP_GR_16_p2(request):
+ """
+ Test Objective : Verify BGP-GR feature when restarting node
+ is a transit router for it's iBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized"
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart": True,
+ "next_hop_self": True,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart": True,
+ "next_hop_self": True,
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "[Step 2] : Test Setup "
+ "[Helper Mode]R3-----R1[Restart Mode]"
+ "--------R6[Helper Mode] initialized"
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_convergence_from_running_config(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_18_p1(request):
+ """
+ Test Objective : Verify that GR helper router deletes stale routes
+ received from restarting node, if GR capability is not present in
+ restarting node's OPEN message.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Helper Mode]R6-----R1[Restart Mode] initialized"
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r6": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r6"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "[Step 2] : Test Setup "
+ "[Helper Mode]R6-----R1[Restart Mode]"
+ "--------R2[Helper Mode] initialized"
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r2"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 3] : Configure R1 to prevent sending EOR")
+
+ # Modify graceful-restart config to prevent sending EOR
+ input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}}
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3)
+
+ # Modify configuration to delete routes
+ network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 5,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Modify graceful-restart config
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Step 4] : Bring up the BGPd daemon on R1 for 30" " seconds..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r6: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying BGP RIB routes
+ dut = "r2"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r6: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py
new file mode 100644
index 0000000..535f272
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-2.py
@@ -0,0 +1,1216 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 7 routers topology
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Graceful Restart on both the routers.
+
+TC_1_2:
+ Verify that EOR message is sent out only after initial convergence
+ Verify whether EOR message is received from all the peers after restart
+TC_3:
+ Verify the selection deferral timer functionality when EOR is not sent
+ by the helper router
+TC_11:
+ Verify that selection-deferral timer sets the maximum time to
+ avoid deadlock during which the best-path
+TC_10:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_15:
+ Test Objective : Test GR scenarios by enabling Graceful Restart
+ for multiple address families..
+TC_16:
+ Test Objective : Verify BGP-GR feature when restarting node
+ is a transit router for it's iBGP peers.
+TC_18:
+ Test Objective : Verify that GR helper router deletes stale routes
+ received from restarting node, if GR capability is not present in
+TC_19:
+ Test Objective : Verify that GR routers keeps all the routes
+ received from restarting node if both the routers are
+TC_26:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_28:
+ Test Objective : Verify if helper node goes down before restarting
+ node comes up online, helper node sets the R-bit to avoid dead-lock
+TC_29:
+ Test Objective : Change timers on the fly, and
+ verify if it takes immediate effect.
+TC_33:
+ Test Objective : Helper router receives same prefixes from two
+ different routers (GR-restarting and GR-disabled). Keeps the
+TC_34_1:
+ Test Objective : Restarting node doesn't preserve forwarding
+ state, helper router should not keep the stale entries.
+TC_34_2:
+ Test Objective : Restarting node doesn't preserve the forwarding
+ state verify the behaviour on helper node, if it still keeps the
+TC_32:
+ Test Objective : Restarting node is connected to multiple helper
+ nodes, one of them doesn't send EOR to restarting router. Verify
+TC_37:
+ Test Objective : Verify if helper node restarts before sending the
+ EOR message, restarting node doesn't wait until stale path timer
+TC_30:
+ Test Objective : Restarting node removes stale routes from Zebra
+ after receiving an EOR from helper router.
+
+"""
+
+import os
+import sys
+import time
+import pytest
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_eor,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_gr_address_family,
+ modify_bgp_config_when_bgpd_down,
+ verify_graceful_restart_timers,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 5
+GR_SELECT_DEFER_TIMER = 5
+GR_STALEPATH_TIMER = 5
+PREFERRED_NEXT_HOP = "link_local"
+NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"]
+NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer)
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def test_BGP_GR_26_p2(request):
+ """
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart": True,
+ "next_hop_self": True,
+ "activate": "ipv6",
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart": True,
+ "next_hop_self": True,
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "graceful-restart-helper": True,
+ "activate": "ipv6",
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "graceful-restart-helper": True,
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r3", peer="r1"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv4Unicast",
+ dut="r1",
+ peer="r3",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv6Unicast",
+ dut="r1",
+ peer="r3",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv4Unicast",
+ dut="r3",
+ peer="r1",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # verify multi address family
+ result = verify_gr_address_family(
+ tgen,
+ topo,
+ addr_type,
+ "ipv6Unicast",
+ dut="r3",
+ peer="r1",
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_28_p1(request):
+ """
+ Test Objective : Verify if helper node goes down before restarting
+ node comes up online, helper node sets the R-bit to avoid dead-lock
+ till SDT expiry.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "Test Case: test_BGP_GR_chaos_28 :"
+ "[Helper Mode]R3-----R1[Restart Mode] initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 1] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 2] : Kill BGPd daemon on R3..")
+
+ # Kill BGPd daemon on R3
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ logger.info("[Step 3] : Start BGPd daemon on R1..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 4] : Start BGPd daemon on R3..")
+
+ # Start BGPd daemon on R3
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ # Verify r_bit
+ for addr_type in ADDR_TYPES:
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r3", peer="r1")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_29_p1(request):
+ """
+ Test Objective : Change timers on the fly, and
+ verify if it takes immediate effect.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_29"
+ " BGP GR [Helper Mode]R3-----R1[Restart Mode]"
+ " and [restart-time 150]R1 initialized"
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verify graceful-restart timers
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER + 5}}
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"timer": {"restart-time": GR_RESTART_TIMER}}
+ }
+ }
+ }
+
+ result = verify_graceful_restart_timers(
+ tgen, topo, addr_type, input_dict_2, dut="r3", peer="r1"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes before shutting down BGPd daemon
+ dut = "r3"
+ input_dict = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 2] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 3] : Wait for {} seconds..".format(GR_RESTART_TIMER))
+
+ # Waiting for GR_RESTART_TIMER
+ sleep(GR_RESTART_TIMER)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes before shutting down BGPd daemon
+ input_dict = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Step 4] : Start BGPd daemon on R1..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_33_p1(request):
+ """
+ Test Objective : Helper router receives same prefixes from two
+ different routers (GR-restarting and GR-disabled). Keeps the
+ stale entry only for GR-restarting node(next-hop is correct).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_33 "
+ "BGP GR "
+ "[Restart Mode]R1--R3[Helper Mode]--R4[Disabled Mode]"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 2] : Advertise same networks from R1 and R4..")
+
+ # Api call to delete advertised networks
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "200.0.20.1/32",
+ "no_of_network": 2,
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "2001::1/128", "no_of_network": 2}
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.0.20.1/32", "no_of_network": 2}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "2001::1/128", "no_of_network": 2}
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ peer1 = "r1"
+ peer2 = "r4"
+ intf1 = topo["routers"][peer1]["links"][dut]["interface"]
+ intf2 = topo["routers"][peer2]["links"][dut]["interface"]
+
+ if addr_type == "ipv4":
+ next_hop_4 = NEXT_HOP_4
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ if addr_type == "ipv6":
+ if "link_local" in PREFERRED_NEXT_HOP:
+ next_hop1 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1)
+ next_hop2 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2)
+
+ next_hop_6 = [next_hop1, next_hop2]
+ else:
+ next_hop_6 = NEXT_HOP_6
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 3] : Kill BGPd daemon on R1 and R4..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ # Kill BGPd daemon on R4
+ kill_router_daemons(tgen, "r4", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ next_hop_6 = ["fd00:0:0:1::1"]
+ if addr_type == "ipv4":
+ next_hop_4 = NEXT_HOP_4[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_4)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ if addr_type == "ipv6":
+ if "link_local" in PREFERRED_NEXT_HOP:
+ next_hop_6 = get_frr_ipv6_linklocal(tgen, peer1, intf=intf1)
+ else:
+ next_hop_6 = NEXT_HOP_6[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, next_hop_6)
+
+ # Verifying RIB routes
+ if addr_type == "ipv4":
+ next_hop_4 = NEXT_HOP_4[1]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_2, next_hop_4, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ if addr_type == "ipv6":
+ if "link_local" in PREFERRED_NEXT_HOP:
+ next_hop_6 = get_frr_ipv6_linklocal(tgen, peer2, intf=intf2)
+ else:
+ next_hop_6 = NEXT_HOP_6[1]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_2, next_hop_6, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Step 4] : Start BGPd daemon on R1 and R4..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ # Start BGPd daemon on R4
+ start_router_daemons(tgen, "r4", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_34_2_p1(request):
+ """
+ Test Objective : Restarting node doesn't preserve the forwarding
+ state verify the behaviour on helper node, if it still keeps the
+ stale routes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_34 "
+ "BGP GR "
+ "[Restart Mode]R1---R3[Helper Mode]"
+ )
+
+ logger.info("[Step 1] : Configure restarting" " router R1 to prevent ")
+ logger.info("[Step 2] : Reset the session" " between R1 and R3..")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True, "disable-eor": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verify f-bit before killing BGPd daemon
+ result = verify_f_bit(tgen, topo, addr_type, input_dict, "r3", "r1")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 3] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 4] : Withdraw/delete the prefixes " "originated from R1..")
+
+ # Api call to delete advertised networks
+ network = {"ipv4": "101.0.20.1/32", "ipv6": "1::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 5,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 5] : Remove the CLI from R1's config to " "set the F-bit..")
+
+ # Modify graceful-restart config not to set f-bit
+ # and write to /etc/frr
+ input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}}
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Step 6] : Bring up the BGPd daemon on R1 again..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verify f-bit after starting BGPd daemon
+ result = verify_f_bit(
+ tgen, topo, addr_type, input_dict, "r3", "r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format(
+ tc_name, result
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py
new file mode 100644
index 0000000..e60552e
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-3.py
@@ -0,0 +1,1367 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 7 routers topology
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Graceful Restart on both the routers.
+
+TC_1_2:
+ Verify that EOR message is sent out only after initial convergence
+ Verify whether EOR message is received from all the peers after restart
+TC_3:
+ Verify the selection deferral timer functionality when EOR is not sent
+ by the helper router
+TC_11:
+ Verify that selection-deferral timer sets the maximum time to
+ avoid deadlock during which the best-path
+TC_10:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_15:
+ Test Objective : Test GR scenarios by enabling Graceful Restart
+ for multiple address families..
+TC_16:
+ Test Objective : Verify BGP-GR feature when restarting node
+ is a transit router for it's iBGP peers.
+TC_18:
+ Test Objective : Verify that GR helper router deletes stale routes
+ received from restarting node, if GR capability is not present in
+TC_19:
+ Test Objective : Verify that GR routers keeps all the routes
+ received from restarting node if both the routers are
+TC_26:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_28:
+ Test Objective : Verify if helper node goes down before restarting
+ node comes up online, helper node sets the R-bit to avoid dead-lock
+TC_29:
+ Test Objective : Change timers on the fly, and
+ verify if it takes immediate effect.
+TC_33:
+ Test Objective : Helper router receives same prefixes from two
+ different routers (GR-restarting and GR-disabled). Keeps the
+TC_34_1:
+ Test Objective : Restarting node doesn't preserve forwarding
+ state, helper router should not keep the stale entries.
+TC_34_2:
+ Test Objective : Restarting node doesn't preserve the forwarding
+ state verify the behaviour on helper node, if it still keeps the
+TC_32:
+ Test Objective : Restarting node is connected to multiple helper
+ nodes, one of them doesn't send EOR to restarting router. Verify
+TC_37:
+ Test Objective : Verify if helper node restarts before sending the
+ EOR message, restarting node doesn't wait until stale path timer
+TC_30:
+ Test Objective : Restarting node removes stale routes from Zebra
+ after receiving an EOR from helper router.
+
+"""
+
+import os
+import sys
+import time
+import pytest
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_eor,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_gr_address_family,
+ modify_bgp_config_when_bgpd_down,
+ verify_graceful_restart_timers,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 5
+GR_SELECT_DEFER_TIMER = 5
+GR_STALEPATH_TIMER = 5
+PREFERRED_NEXT_HOP = "link_local"
+NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"]
+NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer)
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def test_BGP_GR_chaos_34_1_p1(request):
+ """
+ Test Objective : Restarting node doesn't preserve forwarding
+ state, helper router should not keep the stale entries.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_31 "
+ "BGP GR "
+ "[Restart Mode]R1---R3[Helper Mode]"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "preserve-fw-state": True,
+ "timer": {"restart-time": GR_RESTART_TIMER},
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "[Step 1] : Remove the preserve-fw-state command"
+ " from restarting node R1's config"
+ )
+
+ # Configure graceful-restart to set f-bit as False
+ input_dict_2 = {"r1": {"bgp": {"graceful-restart": {"preserve-fw-state": False}}}}
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ logger.info("[Step 2] : Reset the session between R1 and R3..")
+
+ # Reset sessions
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r1")
+
+ result = verify_bgp_convergence_from_running_config(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Verify f-bit after starting BGPd daemon
+ result = verify_f_bit(
+ tgen, topo, addr_type, input_dict_2, "r3", "r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r3: F-bit is set to True\n Error: {}".format(
+ tc_name, result
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ logger.info("[Step 3] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ # Waiting for GR_RESTART_TIMER
+ logger.info("Waiting for {} sec..".format(GR_RESTART_TIMER))
+ sleep(GR_RESTART_TIMER)
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ input_dict = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_32_p1(request):
+ """
+ Test Objective : Restarting node is connected to multiple helper
+ nodes, one of them doesn't send EOR to restarting router. Verify
+ that only after SDT restarting node send EOR to all helper peers
+ excluding the prefixes originated by faulty router.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_32 "
+ "BGP GR "
+ "[Restart Mode]R1---R3&R5[Helper Mode]"
+ )
+
+ logger.info(
+ "[Step 1] : Change the mode on R1 be a restarting" " node on global level"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"graceful-restart": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"next_hop_self": True}}},
+ "r5": {"dest_link": {"r1": {"graceful-restart": True}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"next_hop_self": True}}},
+ "r5": {"dest_link": {"r1": {"graceful-restart": True}}},
+ }
+ }
+ },
+ },
+ }
+ },
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r5": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r5")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r5"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r5"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 2] : Kill BGPd daemon on R1..")
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 3] : Withdraw all the advertised prefixes from R5")
+
+ # Api call to delete advertised networks
+ network = {"ipv4": "105.0.20.1/32", "ipv6": "5::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r5": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 5,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "[Step 4] : Stop the helper router R5 from sending EOR" " message using CLI"
+ )
+
+ # Modify graceful-restart config to prevent sending EOR
+ input_dict_3 = {"r5": {"bgp": {"graceful-restart": {"disable-eor": True}}}}
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ logger.info("[Step 5] : Bring up the BGPd daemon on R1..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verify EOR is disabled
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict_3, dut="r5", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r5: EOR is set to TRUE\n Error: {}".format(
+ tc_name, result
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ input_dict_1 = {key: topo["routers"][key] for key in ["r5"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_37_p1(request):
+ """
+ Test Objective : Verify if helper node restarts before sending the
+ EOR message, restarting node doesn't wait until stale path timer
+ expiry to do the best path selection and sends an EOR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_37 "
+ "BGP GR "
+ "[Restart Mode]R1---R3[Helper Mode]"
+ )
+
+ logger.info(
+ "[Step 1] : Configure restarting router R3 to prevent " "sending an EOR.."
+ )
+
+ logger.info("[Step 2] : Reset the session between R3 and R1..")
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "graceful-restart": {"disable-eor": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verify EOR is disabled
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict, dut="r3", peer="r1", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r3: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying BGP RIB routes after starting BGPd daemon
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 3] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 4] : Start BGPd daemon on R1..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 5] : Kill BGPd daemon on R3..")
+
+ # Kill BGPd daemon on R3
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+
+ # Modify graceful-restart config to prevent sending EOR
+ input_dict_2 = {"r3": {"bgp": {"graceful-restart": {"disable-eor": True}}}}
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ logger.info("[Step 6] : Start BGPd daemon on R3..")
+
+ # Start BGPd daemon on R3
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verify r_bit
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut="r1", peer="r3")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verify EOR is send from R1 to R3
+ input_dict_3 = {"r1": {"bgp": {"graceful-restart": {"disable-eor": True}}}}
+
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict_3, dut="r1", peer="r3", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_chaos_30_p1(request):
+ """
+ Test Objective : Restarting node removes stale routes from Zebra
+ after receiving an EOR from helper router.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Test Case : test_BGP_GR_chaos_30 "
+ "BGP GR [Helper Mode]R3-----R1[Restart Mode] "
+ )
+
+ # Configure graceful-restart and timers
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"preserve-fw-state": True},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes before shutting down BGPd daemon
+ dut = "r1"
+ input_dict = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 2] : Kill BGPd daemon on R1..")
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ logger.info("[Step 3] : Withdraw advertised prefixes from R3...")
+
+ # Api call to delete advertised networks
+ network = {"ipv4": "103.0.20.1/32", "ipv6": "3::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 5,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("[Step 4] : Start BGPd daemon on R1..")
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes before shutting down BGPd daemon
+ input_dict = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_15_p2(request):
+ """
+ Test Objective : Test GR scenarios by enabling Graceful Restart
+ for multiple address families..
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r6": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r6"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info(
+ "[Step 2] : Test Setup "
+ "[Helper Mode]R6-----R1[Restart Mode]"
+ "--------R2[Helper Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def BGP_GR_TC_7_p1(request):
+ """
+ Verify that BGP restarting node deletes all the routes received from peer
+ if BGP Graceful capability is not present in BGP Open message from the
+ peer
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ " Verify route download to RIB: BGP_GR_TC_7 >> "
+ "BGP GR [Helper Mode]R3-----R1[Restart Mode] "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r1": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r1": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes received from router R1
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("R1 goes for reload")
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ # Change the configuration on router R1
+ input_dict_2 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Change the configuration on R1
+ network = {"ipv4": "103.0.20.1/32", "ipv6": "3::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 5,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("R1 is about to come up now")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+ logger.info("R1 is UP Now")
+
+ # Wait for RIB stale timeout
+ logger.info("Verify routes are not present" "in restart router")
+
+ for addr_type in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r3"]}
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: routes are still present in ZEBRA\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/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py
new file mode 100644
index 0000000..1df77eb
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo2/test_bgp_gr_functionality_topo2-4.py
@@ -0,0 +1,1024 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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 BGP Graceful Restart functionality.
+Basic Common Test steps for all the test case below :
+- Create topology (setup module)
+ Creating 7 routers topology
+- Bring up topology
+- Verify for bgp to converge
+- Configure BGP Graceful Restart on both the routers.
+
+TC_1_2:
+ Verify that EOR message is sent out only after initial convergence
+ Verify whether EOR message is received from all the peers after restart
+TC_3:
+ Verify the selection deferral timer functionality when EOR is not sent
+ by the helper router
+TC_11:
+ Verify that selection-deferral timer sets the maximum time to
+ avoid deadlock during which the best-path
+TC_10:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_15:
+ Test Objective : Test GR scenarios by enabling Graceful Restart
+ for multiple address families..
+TC_16:
+ Test Objective : Verify BGP-GR feature when restarting node
+ is a transit router for it's iBGP peers.
+TC_18:
+ Test Objective : Verify that GR helper router deletes stale routes
+ received from restarting node, if GR capability is not present in
+TC_19:
+ Test Objective : Verify that GR routers keeps all the routes
+ received from restarting node if both the routers are
+TC_26:
+ Test Objective : Test GR scenarios on helper router by enabling
+ Graceful Restart for multiple address families.
+TC_28:
+ Test Objective : Verify if helper node goes down before restarting
+ node comes up online, helper node sets the R-bit to avoid dead-lock
+TC_29:
+ Test Objective : Change timers on the fly, and
+ verify if it takes immediate effect.
+TC_33:
+ Test Objective : Helper router receives same prefixes from two
+ different routers (GR-restarting and GR-disabled). Keeps the
+TC_34_1:
+ Test Objective : Restarting node doesn't preserve forwarding
+ state, helper router should not keep the stale entries.
+TC_34_2:
+ Test Objective : Restarting node doesn't preserve the forwarding
+ state verify the behaviour on helper node, if it still keeps the
+TC_32:
+ Test Objective : Restarting node is connected to multiple helper
+ nodes, one of them doesn't send EOR to restarting router. Verify
+TC_37:
+ Test Objective : Verify if helper node restarts before sending the
+ EOR message, restarting node doesn't wait until stale path timer
+TC_30:
+ Test Objective : Restarting node removes stale routes from Zebra
+ after receiving an EOR from helper router.
+
+These tests have been broken up into 4 sub python scripts because
+the totality of run time for this script was greater than 10 minutes
+"""
+
+import os
+import sys
+import time
+import pytest
+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("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_eor,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_gr_address_family,
+ modify_bgp_config_when_bgpd_down,
+ verify_graceful_restart_timers,
+ verify_bgp_convergence_from_running_config,
+)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ step,
+ get_frr_ipv6_linklocal,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 5
+GR_SELECT_DEFER_TIMER = 5
+GR_STALEPATH_TIMER = 5
+PREFERRED_NEXT_HOP = "link_local"
+NEXT_HOP_4 = ["192.168.1.1", "192.168.4.2"]
+NEXT_HOP_6 = ["fd00:0:0:1::1", "fd00:0:0:4::2"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+
+ 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...
+ json_file = "{}/bgp_gr_topojson_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut, peer):
+ """
+ This function groups the repetitive function calls into one function.
+ """
+
+ logger.info("configure_gr_followed_by_clear: dut %s peer %s", dut, peer)
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][peer]["links"][dut][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, dut, neighbor=neighbor)
+
+ for addr_type in ADDR_TYPES:
+ neighbor = topo["routers"][dut]["links"][peer][addr_type].split("/")[0]
+ clear_bgp(tgen, addr_type, peer, neighbor=neighbor)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+
+def next_hop_per_address_family(tgen, dut, peer, addr_type, next_hop_dict):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}-link1".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+def test_BGP_GR_TC_23_p1(request):
+ """
+ Verify that helper routers are deleting stale routes after stale route
+ timer's expiry. If all the routes are not received from restating node
+ after restart.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "Verify Stale Routes are deleted on helper: BGP_GR_TC_23 >> "
+ "BGP GR [Helper Mode]R1-----R2[Restart Mode] "
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "graceful-restart": {"timer": {"stalepath-time": GR_STALEPATH_TIMER}},
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ },
+ "r2": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"graceful-restart": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"graceful-restart": True}}}
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes received from router R1
+ dut = "r1"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("R2 goes for reload")
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ # Modify configuration to delete routes and include disable-eor
+ input_dict_3 = {"r2": {"bgp": {"graceful-restart": {"disable-eor": True}}}}
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3)
+
+ # Modify configuration to delete routes and include disable-eor
+ network = {"ipv4": "102.0.20.1/32", "ipv6": "2::1/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": network[addr_type],
+ "no_of_network": 3,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("BGPd comes up for r2")
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ # Wait for stalepath timer
+ logger.info("Waiting for stalepath timer({} sec..)".format(GR_STALEPATH_TIMER))
+ sleep(GR_STALEPATH_TIMER)
+
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r2")
+
+ # Verifying RIB routes
+ dut = "r1"
+ network = {"ipv4": "102.0.20.4/32", "ipv6": "2::4/128"}
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {"network": network[addr_type], "no_of_network": 2}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ # Verify EOR on helper router
+ result = verify_eor(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: EOR is set to True\n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP RIB routes received from router R1
+ dut = "r1"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_20_p1(request):
+ """
+ Test Objective : Verify that GR routers delete all the routes
+ received from a node if both the routers are configured as GR
+ helper node
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Restart Mode]R3-----R1[Restart Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in BGP RIB\n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: routes are still present in ZEBRA\n Error: {}".format(tc_name, result)
+ )
+ logger.info(" Expected behavior: {}".format(result))
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_21_p2(request):
+ """
+ Test Objective : VVerify BGP-GR feature when helper node is
+ a transit router for it's eBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Helper Mode]R6-----R1[Restart Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r1": {"graceful-restart-disable": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r6": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r6": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r6")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r6"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info(
+ "[Step 2] : Test Setup "
+ "[Restart Mode]R2-----[Helper Mode]R1[Disable Mode]"
+ "--------R6[Helper Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes after bringing up BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r6"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_GR_22_p2(request):
+ """
+ Test Objective : Verify BGP-GR feature when helper node
+ is a transit router for it's iBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Check router status
+ check_router_status(tgen)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ logger.info(
+ "[Step 1] : Test Setup " "[Helper Mode]R3-----R1[Restart Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart-disable": True,
+ "next_hop_self": True,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "graceful-restart-disable": True,
+ "next_hop_self": True,
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r3")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r3"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info(
+ "[Step 2] : Test Setup "
+ "[Restart Mode]R2-----[Helper Mode]R1[Disable Mode]"
+ "--------R3[Helper Mode] Initialized"
+ )
+
+ # Configure graceful-restart
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r2": {"bgp": {"graceful-restart": {"graceful-restart": True}}},
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name, dut="r1", peer="r2")
+
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut="r1", peer="r2"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Kill BGPd daemon on R1
+ kill_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Start BGPd daemon on R1
+ start_router_daemons(tgen, "r2", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_1 = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying BGP RIB routes
+ dut = "r3"
+ input_dict_2 = {key: topo["routers"][key] for key in ["r2"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying RIB routes before shutting down BGPd daemon
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ 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/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json
new file mode 100644
index 0000000..84ddc61
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.json
@@ -0,0 +1,222 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py
new file mode 100644
index 0000000..6bf8b96
--- /dev/null
+++ b/tests/topotests/bgp_gr_functionality_topo3/bgp_gr_functionality_topo3.py
@@ -0,0 +1,554 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2019 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.
+#
+
+import os
+import sys
+import time
+import pytest
+from time import sleep
+
+import traceback
+import ipaddress
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join("../"))
+sys.path.append(os.path.join("../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topojson import build_config_from_json
+from lib.bgp import (
+ clear_bgp,
+ verify_bgp_rib,
+ verify_graceful_restart,
+ create_router_bgp,
+ verify_r_bit,
+ verify_eor,
+ verify_f_bit,
+ verify_bgp_convergence,
+ verify_gr_address_family,
+ modify_bgp_config_when_bgpd_down,
+ verify_graceful_restart_timers,
+ verify_bgp_convergence_from_running_config,
+)
+# Import common_config to use commomnly used APIs
+from lib.common_config import (create_common_configuration,
+ InvalidCLIError, retry,
+ generate_ips, FRRCFG_FILE,
+ find_interface_with_greater_ip,
+ check_address_types,
+ validate_ip_address,
+ run_frr_cmd,
+ get_frr_ipv6_linklocal)
+
+from lib.common_config import (
+ write_test_header,
+ reset_config_on_routers,
+ start_topology,
+ kill_router_daemons,
+ start_router_daemons,
+ verify_rib,
+ check_address_types,
+ write_test_footer,
+ check_router_status,
+ step,
+ get_frr_ipv6_linklocal,
+ create_static_routes,
+ required_linux_kernel_version,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+GR_RESTART_TIMER = 5
+GR_SELECT_DEFER_TIMER = 5
+GR_STALEPATH_TIMER = 5
+# Global variables
+# STATIC_ROUTES=[]
+NETWORK1_1 = {"ipv4": "192.0.2.1/32", "ipv6": "2001:DB8::1:1/128"}
+NETWORK1_2 = {"ipv4": "192.0.2.2/32", "ipv6": "2001:DB8::2:1/128"}
+NETWORK2_1 = {"ipv4": "192.0.2.3/32", "ipv6": "2001:DB8::3:1/128"}
+NETWORK2_2 = {"ipv4": "192.0.2.4/32", "ipv6": "2001:DB8::4:1/128"}
+NETWORK3_1 = {"ipv4": "192.0.2.5/32", "ipv6": "2001:DB8::5:1/128"}
+NETWORK3_2 = {"ipv4": "192.0.2.6/32", "ipv6": "2001:DB8::6:1/128"}
+NETWORK4_1 = {"ipv4": "192.0.2.7/32", "ipv6": "2001:DB8::7:1/128"}
+NETWORK4_2 = {"ipv4": "192.0.2.8/32", "ipv6": "2001:DB8::8:1/128"}
+NETWORK5_1 = {"ipv4": "192.0.2.9/32", "ipv6": "2001:DB8::9:1/128"}
+NETWORK5_2 = {"ipv4": "192.0.2.10/32", "ipv6": "2001:DB8::10:1/128"}
+
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,
+ dut, peer):
+ """
+ result = configure_gr_followed_by_clear(tgen, topo, dut)
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+ """
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, dut)
+
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, peer)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ return True
+
+def verify_stale_routes_list(tgen, addr_type, dut, input_dict):
+ """
+ This API is use verify Stale routes on refering the network with next hop value
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: input dut router name
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict` : input dict, has details of static routes
+ Usage
+ -----
+ dut = 'r1'
+ input_dict = {
+ "r3": {
+ "static_routes": [
+
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "no_of_ip": 2,
+ "vrf": "RED"
+ }
+ ]
+ }
+ }
+
+ result = verify_stale_routes_list(tgen, addr_type, dut, input_dict)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: verify_stale_routes_list()")
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ list1 = []
+ list2 = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+ # Verifying RIB routes
+ command = "show bgp"
+ # Static routes
+ sleep(2)
+ logger.info('Checking router {} BGP RIB:'.format(dut))
+ if 'static_routes' in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["static_routes"]
+ for static_route in static_routes:
+ found_routes = []
+ missing_routes = []
+ st_found = False
+ nh_found = False
+ vrf = static_route.setdefault("vrf", None)
+ community = static_route.setdefault("community", None)
+ largeCommunity = \
+ static_route.setdefault("largeCommunity", None)
+ if vrf:
+ cmd = "{} vrf {} {}".\
+ format(command, vrf, addr_type)
+ if community:
+ cmd = "{} community {}".\
+ format(cmd, community)
+ if largeCommunity:
+ cmd = "{} large-community {}".\
+ format(cmd, largeCommunity)
+ else:
+ cmd = "{} {}".\
+ format(command, addr_type)
+ cmd = "{} json".format(cmd)
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) == False:
+ errormsg = "[DUT: {}]: No route found in rib of router". \
+ format(router)
+ return errormsg
+ elif "warning" in rib_routes_json:
+ errormsg = "[DUT: {}]: {}". \
+ format(router, rib_routes_json["warning"])
+ return errormsg
+ network = static_route["network"]
+ if "no_of_ip" in static_route:
+ no_of_ip = static_route["no_of_ip"]
+ else:
+ no_of_ip = 1
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(st_rt))
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+ if st_rt in rib_routes_json["routes"]:
+ st_found = True
+
+ found_routes.append(st_rt)
+ for mnh in range(0, len(rib_routes_json[
+ 'routes'][st_rt])):
+ found_hops.append([rib_r[
+ "ip"] for rib_r in rib_routes_json[
+ 'routes'][st_rt][
+ mnh]["nexthops"]])
+ return found_hops
+ else:
+ return 'error msg - no hops found'
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+
+ 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...
+ json_file = "{}/bgp_gr_functionality_topo3.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+################################################################################
+#
+# TEST CASES
+#
+################################################################################
+def test_bgp_gr_stale_routes(request):
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ step("Verify the router failures")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Creating 5 static Routes in Router R3 with NULL0 as Next hop")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [{
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type]
+ }]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, 'Testcase {} : Failed \n Error: {}'.format(
+ tc_name, result)
+ step("verifying Created Route at R3 in VRF default")
+ for addr_type in ADDR_TYPES:
+ dut = 'r3'
+ input_dict_1= {'r3': topo['routers']['r3']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+ #done
+ step("verifying Created Route at R2 in VRF default")
+ for addr_type in ADDR_TYPES:
+ dut = 'r2'
+ input_dict_1= {'r2': topo['routers']['r2']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+ step("importing vrf RED on R2 under Address Family")
+ for addr_type in ADDR_TYPES:
+ input_import_vrf={
+ "r2": {
+ "bgp": [
+ {
+ "local_as": 200,
+ "vrf": "RED",
+ "address_family": {addr_type: {"unicast": {"import": {"vrf": "default"}}}},
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo,input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ #done
+ step("verifying static Routes at R2 in VRF RED")
+ for addr_type in ADDR_TYPES:
+ dut = 'r2'
+ input_dict_1= {'r2': topo['routers']['r2']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("verifying static Routes at R1 in VRF RED")
+ for addr_type in ADDR_TYPES:
+ dut = 'r1'
+ input_dict_1= {'r1': topo['routers']['r1']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("Configuring Graceful restart at R2 and R3 ")
+ input_dict = {
+ "r2": {
+
+ "bgp": {
+ "local_as": "200",
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "graceful-restart": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,dut='r2', peer='r3')
+
+
+
+ step("verify Graceful restart at R2")
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(tgen, topo, addr_type, input_dict,
+ dut='r2', peer='r3')
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("verify Graceful restart at R3")
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(tgen, topo, addr_type, input_dict,
+ dut='r3', peer='r2')
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("Configuring Graceful-restart-disable at R3")
+ input_dict = {
+ "r2": {
+
+ "bgp": {
+ "local_as": "200",
+ "graceful-restart": {
+ "graceful-restart": False,
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "graceful-restart": {
+ "graceful-restart": False
+ }
+ }
+ }
+ }
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,dut='r3', peer='r2')
+
+ step("Verify Graceful-restart-disable at R3")
+ for addr_type in ADDR_TYPES:
+ result = verify_graceful_restart(tgen, topo, addr_type, input_dict,
+ dut='r3', peer='r2')
+ assert result is True, \
+ "Testcase {} :Failed \n Error {}". \
+ format(tc_name, result)
+
+ for iteration in range(5):
+ step("graceful-restart-disable:True at R3")
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": True,
+ }
+ }
+ }
+ }
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,
+ dut='r3', peer='r2')
+
+ step("Verifying Routes at R2 on enabling GRD")
+ dut = 'r2'
+ for addr_type in ADDR_TYPES:
+ input_dict_1= {'r2': topo['routers']['r2']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify stale Routes in Router R2 enabling GRD")
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r3": {
+ "static_routes": [
+
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "no_of_ip": 2,
+ "vrf": "RED"
+ }
+ ]
+ }
+ }
+ bgp_rib_next_hops = verify_stale_routes_list(
+ tgen, addr_type, dut, verify_nh_for_static_rtes)
+ assert (len(bgp_rib_next_hops)== 1) is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_rib_next_hops,expected=True)
+
+ step("graceful-restart-disable:False at R3")
+ input_dict = {
+ "r3": {
+ "bgp": {
+ "graceful-restart": {
+ "graceful-restart-disable": False,
+ }
+ }
+ }
+ }
+ configure_gr_followed_by_clear(tgen, topo, input_dict, tc_name,
+ dut='r3', peer='r2')
+
+ step("Verifying Routes at R2 on disabling GRD")
+ dut = 'r2'
+ for addr_type in ADDR_TYPES:
+ input_dict_1= {'r2': topo['routers']['r2']}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify stale Routes in Router R2 on disabling GRD")
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r3": {
+ "static_routes": [
+
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "no_of_ip": 2,
+ "vrf": "RED"
+ }
+ ]
+ }
+ }
+ bgp_rib_next_hops = verify_stale_routes_list(tgen, addr_type, dut, verify_nh_for_static_rtes)
+
+ stale_route_status=len(bgp_rib_next_hops)== 1
+ assert stale_route_status is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, stale_route_status,expected=True)
+ write_test_footer(tc_name)
+
diff --git a/tests/topotests/bgp_gr_notification/__init__.py b/tests/topotests/bgp_gr_notification/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/__init__.py
diff --git a/tests/topotests/bgp_gr_notification/r1/bgpd.conf b/tests/topotests/bgp_gr_notification/r1/bgpd.conf
new file mode 100644
index 0000000..6119f67
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ neighbor 192.168.255.2 remote-as external
+ neighbor 192.168.255.2 timers 1 3
+ neighbor 192.168.255.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gr_notification/r1/zebra.conf b/tests/topotests/bgp_gr_notification/r1/zebra.conf
new file mode 100644
index 0000000..091794f
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gr_notification/r2/bgpd.conf b/tests/topotests/bgp_gr_notification/r2/bgpd.conf
new file mode 100644
index 0000000..05e17f0
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r2/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp hard-administrative-reset
+ bgp graceful-restart
+ neighbor 192.168.255.1 remote-as external
+ neighbor 192.168.255.1 timers 1 3
+ neighbor 192.168.255.1 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gr_notification/r2/zebra.conf b/tests/topotests/bgp_gr_notification/r2/zebra.conf
new file mode 100644
index 0000000..1fcccbe
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.2/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
new file mode 100644
index 0000000..3519e5c
--- /dev/null
+++ b/tests/topotests/bgp_gr_notification/test_bgp_gr_notification.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+#
+# bgp_gr_notification.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+TC1: Disable the link between R1-R2 and wait for HoldTimerExpire notification:
+ 1) Check if R2 sent HoldTimerExpired notification
+ 2) Check if the routes are retained at R2
+TC2: Trigger `clear bgp` (Administrative Reset):
+ `bgp hard-administrative-reset` disabled:
+ a) Check if Administrative Reset notification was sent from R2
+ b) Routes should be retained on R1
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_hold_timer_expired_gr():
+ # TC1
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _disable_link_r1_r2():
+ r1.cmd_raises("ip link set down dev r1-eth0")
+
+ def _enable_link_r1_r2():
+ r1.cmd_raises("ip link set up dev r1-eth0")
+
+ def _bgp_check_hold_timer_expire_reason():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "lastNotificationReason": "Hold Timer Expired",
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_hold_timer_expire_stale():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "stale": True,
+ "valid": True,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Disable the link between R1-R2")
+ _disable_link_r1_r2()
+
+ step("Check if R2 sent HoldTimerExpire notification to R1")
+ test_func = functools.partial(_bgp_check_hold_timer_expire_reason)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see Hold Timer Expired notification from R2 on R1"
+
+ step("Check if the routes are retained at R2")
+ test_func = functools.partial(_bgp_check_hold_timer_expire_stale)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see retained stale routes on R2"
+
+ step("Enable the link between R1-R2")
+ _enable_link_r1_r2()
+
+
+def test_bgp_administrative_reset_gr():
+ # TC2
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show ip bgp neighbor 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_hard_reset():
+ output = json.loads(r1.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "lastNotificationReason": "Cease/Administrative Reset",
+ "lastNotificationHardReset": False,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_gr_notification_stale():
+ output = json.loads(r1.vtysh_cmd("show bgp ipv4 unicast 172.16.255.2/32 json"))
+ expected = {
+ "paths": [
+ {
+ "stale": True,
+ "valid": True,
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_clear_r1_and_shutdown():
+ r2.vtysh_cmd(
+ """
+ clear ip bgp 192.168.255.1
+ configure terminal
+ router bgp
+ neighbor 192.168.255.1 shutdown
+ """
+ )
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Reset and shutdown R1")
+ _bgp_clear_r1_and_shutdown()
+
+ step("Check if Hard Reset notification wasn't sent from R2")
+ test_func = functools.partial(_bgp_check_hard_reset)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to send Administrative Reset notification from R2"
+
+ step("Check if stale routes are retained on R1")
+ test_func = functools.partial(_bgp_check_gr_notification_stale)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see retained stale routes on R1"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/__init__.py b/tests/topotests/bgp_gr_restart_retain_routes/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/__init__.py
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf
new file mode 100644
index 0000000..50d1583
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/bgpd.conf
@@ -0,0 +1,11 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp graceful-restart preserve-fw-state
+ neighbor 192.168.255.2 remote-as external
+ neighbor 192.168.255.2 timers 1 3
+ neighbor 192.168.255.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf
new file mode 100644
index 0000000..e65bfb2
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf
new file mode 100644
index 0000000..97418ca
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp graceful-restart preserve-fw-state
+ neighbor 192.168.255.1 remote-as external
+ neighbor 192.168.255.1 timers 1 3
+ neighbor 192.168.255.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf
new file mode 100644
index 0000000..c03dd7e
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
diff --git a/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py
new file mode 100644
index 0000000..52ce0d5
--- /dev/null
+++ b/tests/topotests/bgp_gr_restart_retain_routes/test_bgp_gr_restart_retain_routes.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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 if routes are retained during BGP restarts.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step, stop_router
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_gr_restart_retain_routes():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 neighbors 192.168.255.1 json"))
+ expected = {
+ "192.168.255.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_bgp_retained_routes():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json"))
+ expected = {"paths": [{"stale": True}]}
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_kernel_retained_routes():
+ output = (
+ r2.cmd("ip route show 172.16.255.1/32 proto bgp dev r2-eth0")
+ .replace("\n", "")
+ .rstrip()
+ )
+ expected = "172.16.255.1 via 192.168.255.1 metric 20"
+ diff = topotest.get_textdiff(
+ output, expected, "Actual IP Routing Table", "Expected IP RoutingTable"
+ )
+ if diff:
+ return False
+ return True
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Restart R1")
+ stop_router(tgen, "r1")
+
+ step("Check if routes (BGP) are retained at R2")
+ test_func = functools.partial(_bgp_check_bgp_retained_routes)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP retained routes on R2"
+
+ step("Check if routes (Kernel) are retained at R2")
+ assert _bgp_check_kernel_retained_routes() == True
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gshut/__init__.py b/tests/topotests/bgp_gshut/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_gshut/__init__.py
diff --git a/tests/topotests/bgp_gshut/r1/bgp_route_1.json b/tests/topotests/bgp_gshut/r1/bgp_route_1.json
new file mode 100644
index 0000000..f3921b2
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r1/bgp_route_1.json
@@ -0,0 +1,12 @@
+{
+ "prefix":"13.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "metric":0,
+ "locPrf":100,
+ "valid":true
+ }
+ ]
+}
+
diff --git a/tests/topotests/bgp_gshut/r1/bgp_route_2.json b/tests/topotests/bgp_gshut/r1/bgp_route_2.json
new file mode 100644
index 0000000..754a0ed
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r1/bgp_route_2.json
@@ -0,0 +1,17 @@
+{
+ "prefix":"13.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "metric":0,
+ "locPrf":0,
+ "valid":true,
+ "community":{
+ "string":"graceful-shutdown",
+ "list":[
+ "gracefulShutdown"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_gshut/r1/bgpd.conf b/tests/topotests/bgp_gshut/r1/bgpd.conf
new file mode 100644
index 0000000..ab6f47a
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r1/bgpd.conf
@@ -0,0 +1,10 @@
+! exit1
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.255.1 remote-as 65001
+ neighbor 192.168.255.1 timers connect 10
+ address-family ipv4 unicast
+ network 11.1.1.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gshut/r1/zebra.conf b/tests/topotests/bgp_gshut/r1/zebra.conf
new file mode 100644
index 0000000..9904bb4
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r1/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gshut/r2/bgp_sum_1.json b/tests/topotests/bgp_gshut/r2/bgp_sum_1.json
new file mode 100644
index 0000000..9d8948a
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r2/bgp_sum_1.json
@@ -0,0 +1,15 @@
+{
+"ipv4Unicast":{
+ "peers":{
+ "192.168.254.2":{
+ "remoteAs":65003,
+ "state":"Established"
+ },
+ "192.168.255.2":{
+ "remoteAs":65001,
+ "state":"Established"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_gshut/r2/bgp_sum_2.json b/tests/topotests/bgp_gshut/r2/bgp_sum_2.json
new file mode 100644
index 0000000..7183db6
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r2/bgp_sum_2.json
@@ -0,0 +1,15 @@
+{
+"ipv4Unicast":{
+ "peers":{
+ "192.168.252.2":{
+ "remoteAs":65005,
+ "state":"Established"
+ },
+ "192.168.253.2":{
+ "remoteAs":65004,
+ "state":"Established"
+ }
+ },
+ "totalPeers":2
+}
+}
diff --git a/tests/topotests/bgp_gshut/r2/bgpd.conf b/tests/topotests/bgp_gshut/r2/bgpd.conf
new file mode 100644
index 0000000..b0ca4e6
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r2/bgpd.conf
@@ -0,0 +1,20 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.255.2 timers connect 10
+ neighbor 192.168.254.2 timers connect 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
+router bgp 65001 vrf vrf1
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.253.2 remote-as 65004
+ neighbor 192.168.253.2 timers connect 10
+ neighbor 192.168.252.2 remote-as 65005
+ neighbor 192.168.252.2 timers connect 10
+!
diff --git a/tests/topotests/bgp_gshut/r2/zebra.conf b/tests/topotests/bgp_gshut/r2/zebra.conf
new file mode 100644
index 0000000..0da0501
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r2/zebra.conf
@@ -0,0 +1,13 @@
+! spine
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+interface r2-eth1
+ ip address 192.168.254.1/30
+!
+interface r2-eth2 vrf vrf1
+ ip address 192.168.253.1/30
+!
+interface r2-eth3 vrf vrf1
+ ip address 192.168.252.1/30
+!
diff --git a/tests/topotests/bgp_gshut/r3/bgp_route_1.json b/tests/topotests/bgp_gshut/r3/bgp_route_1.json
new file mode 100644
index 0000000..94de01a
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r3/bgp_route_1.json
@@ -0,0 +1,9 @@
+{
+ "prefix":"11.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "valid":true
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_gshut/r3/bgp_route_2.json b/tests/topotests/bgp_gshut/r3/bgp_route_2.json
new file mode 100644
index 0000000..f918265
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r3/bgp_route_2.json
@@ -0,0 +1,16 @@
+{
+ "prefix":"11.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "locPrf":0,
+ "valid":true,
+ "community":{
+ "string":"graceful-shutdown",
+ "list":[
+ "gracefulShutdown"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_gshut/r3/bgpd.conf b/tests/topotests/bgp_gshut/r3/bgpd.conf
new file mode 100644
index 0000000..5d7c0cd
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r3/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 3 9
+ neighbor 192.168.254.1 remote-as 65001
+ neighbor 192.168.254.1 timers connect 10
+ address-family ipv4 unicast
+ network 13.1.1.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gshut/r3/zebra.conf b/tests/topotests/bgp_gshut/r3/zebra.conf
new file mode 100644
index 0000000..f490d97
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r3/zebra.conf
@@ -0,0 +1,9 @@
+! exit2
+interface lo
+ ip address 172.16.254.254/32
+!
+interface r3-eth0
+ ip address 192.168.254.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gshut/r4/bgpd.conf b/tests/topotests/bgp_gshut/r4/bgpd.conf
new file mode 100644
index 0000000..375f383
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r4/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65004
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 3 9
+ neighbor 192.168.253.1 remote-as 65001
+ neighbor 192.168.253.1 timers connect 10
+ address-family ipv4 unicast
+ network 14.1.1.1/32
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_gshut/r4/zebra.conf b/tests/topotests/bgp_gshut/r4/zebra.conf
new file mode 100644
index 0000000..baba04c
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r4/zebra.conf
@@ -0,0 +1,9 @@
+! exit2
+interface lo
+ ip address 172.16.253.254/32
+!
+interface r4-eth0
+ ip address 192.168.253.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gshut/r5/bgp_route_1.json b/tests/topotests/bgp_gshut/r5/bgp_route_1.json
new file mode 100644
index 0000000..4e6fd79
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r5/bgp_route_1.json
@@ -0,0 +1,9 @@
+{
+ "prefix":"14.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "valid":true
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_gshut/r5/bgp_route_2.json b/tests/topotests/bgp_gshut/r5/bgp_route_2.json
new file mode 100644
index 0000000..980d8de
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r5/bgp_route_2.json
@@ -0,0 +1,16 @@
+{
+ "prefix":"14.1.1.1\/32",
+ "paths":[
+ {
+ "origin":"IGP",
+ "locPrf":0,
+ "valid":true,
+ "community":{
+ "string":"graceful-shutdown",
+ "list":[
+ "gracefulShutdown"
+ ]
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_gshut/r5/bgpd.conf b/tests/topotests/bgp_gshut/r5/bgpd.conf
new file mode 100644
index 0000000..15b49f5
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r5/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65005
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.252.1 remote-as 65001
+ neighbor 192.168.252.1 timers connect 10
+!
diff --git a/tests/topotests/bgp_gshut/r5/zebra.conf b/tests/topotests/bgp_gshut/r5/zebra.conf
new file mode 100644
index 0000000..c4cbd52
--- /dev/null
+++ b/tests/topotests/bgp_gshut/r5/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.252.254/32
+!
+interface r5-eth0
+ ip address 192.168.252.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_gshut/test_bgp_gshut.py b/tests/topotests/bgp_gshut/test_bgp_gshut.py
new file mode 100644
index 0000000..764252d
--- /dev/null
+++ b/tests/topotests/bgp_gshut/test_bgp_gshut.py
@@ -0,0 +1,356 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_gshut.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Vivek Venkatraman <vivek@nvidia.com>
+#
+# 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 the ability to initiate and stop BGP graceful shutdown.
+Test both the vrf-specific and global configuration and operation.
+
+r1
+|
+r2----r3
+| \
+| \
+r4 r5
+
+
+r2 is UUT and peers with r1 and r3 in default bgp instance and
+with r4 and r5 in vrf vrf1.
+r1-r2 peering is iBGP and the other peerings are eBGP.
+
+Check r2 initial convergence in default table
+Define update-delay with max-delay in the default bgp instance on r2
+Shutdown peering on r1 toward r2 so that delay timers can be exercised
+Clear bgp neighbors on r2 and then check for the 'in progress' indicator
+Check that r2 only installs route learned from r4 after the max-delay timer expires
+Define update-delay with max-delay and estabish-wait and check json output showing set
+Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time
+Remove update-delay timer on r2 to verify that it goes back to normal behavior
+Clear neighbors on r2 and check that route install time on r2 does not delay
+Define global bgp update-delay with max-delay and establish-wait on r2
+Check that r2 default instance and vrf1 have the max-delay and establish set
+Clear neighbors on r2 and check route-install time is after the establish-wait timer
+
+Note that the keepalive/hold times were changed to 3/9 and the connect retry timer
+to 10 to improve the odds the convergence timing in this test case is useful in the
+event of packet loss.
+"""
+
+import os
+import re
+import sys
+import json
+import pytest
+import platform
+from functools import partial
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 6):
+ 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r5"])
+
+
+def _run_cmd_and_check(router, cmd, results_file, retries=100, intvl=0.5):
+ json_file = "{}/{}".format(CWD, results_file)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(topotest.router_json_cmp, router, cmd, expected)
+ return topotest.run_and_expect(test_func, None, retries, intvl)
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.5") < 0:
+ tgen.errors = "Linux kernel version of at least 4.5 needed for bgp-gshut tests"
+ pytest.skip(tgen.errors)
+
+ # Configure vrf and its slaves in the kernel on r2
+ r2 = tgen.gears["r2"]
+ r2.run("ip link add vrf1 type vrf table 1000")
+ r2.run("ip link set vrf1 up")
+ r2.run("ip link set r2-eth2 master vrf1")
+ r2.run("ip link set r2-eth3 master vrf1")
+
+ # Load FRR config and initialize all routers
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+ # Basic peering test to see if things are ok
+ _, result = _run_cmd_and_check(r2, "show ip bgp summary json", "r2/bgp_sum_1.json")
+ assertmsg = "R2: Basic sanity test after init failed -- global peerings not up"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r2, "show ip bgp vrf vrf1 summary json", "r2/bgp_sum_2.json"
+ )
+ assertmsg = "R2: Basic sanity test after init failed -- VRF peerings not up"
+ assert result is None, assertmsg
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_gshut():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+ r5 = tgen.gears["r5"]
+
+ # Verify initial route states
+ logger.info("\nVerify initial route states")
+
+ _, result = _run_cmd_and_check(
+ r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
+ )
+ assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
+ )
+ assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
+ )
+ assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ logger.info("\nInitial route states are as expected")
+
+ # "Test #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers"
+ logger.info(
+ "\nTest #1: Enable BGP-wide graceful-shutdown on R2 and check routes on peers"
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ bgp graceful-shutdown
+ """
+ )
+
+ # R1, R3 and R5 should see routes from R2 with GSHUT. In addition,
+ # R1 should see LOCAL_PREF of 0
+ _, result = _run_cmd_and_check(
+ r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_2.json"
+ )
+ assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_2.json"
+ )
+ assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json"
+ )
+ assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ logger.info(
+ "\nTest #1: Successful, routes have GSHUT and/or LPREF of 0 as expected"
+ )
+
+ # "Test #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers"
+ logger.info(
+ "\nTest #2: Turn off BGP-wide graceful-shutdown on R2 and check routes on peers"
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ no bgp graceful-shutdown
+ """
+ )
+
+ # R1, R3 and R5 should see routes from R2 with their original attributes
+ _, result = _run_cmd_and_check(
+ r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
+ )
+ assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
+ )
+ assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
+ )
+ assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ logger.info(
+ "\nTest #2: Successful, routes have their original attributes with default LPREF and without GSHUT"
+ )
+
+ # "Test #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers"
+ logger.info(
+ "\nTest #3: Enable graceful-shutdown on R2 only in VRF1 and check routes on peers"
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001 vrf vrf1
+ bgp graceful-shutdown
+ """
+ )
+
+ # R1 and R3 should see no change to their routes
+ _, result = _run_cmd_and_check(
+ r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
+ )
+ assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
+ )
+ assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ # R5 should see routes from R2 with GSHUT.
+ _, result = _run_cmd_and_check(
+ r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_2.json"
+ )
+ assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ logger.info("\nTest #3: Successful, only VRF peers like R5 see routes with GSHUT")
+
+ # "Test #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1"
+ logger.info(
+ "\nTest #4: Try to enable BGP-wide graceful-shutdown on R2 while it is configured in VRF1"
+ )
+
+ ret = r2.vtysh_cmd(
+ """
+ configure terminal
+ bgp graceful-shutdown
+ """
+ )
+
+ # This should fail
+ assertmsg = "R2: BGP-wide graceful-shutdown config not rejected even though it is enabled in VRF1"
+ assert (
+ re.search("global graceful-shutdown not permitted", ret) is not None
+ ), assertmsg
+
+ logger.info(
+ "\nTest #4: Successful, BGP-wide graceful-shutdown rejected as it is enabled in VRF"
+ )
+
+ # "Test #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers"
+ logger.info(
+ "\nTest #5: Turn off graceful-shutdown on R2 in VRF1 and check routes on peers"
+ )
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001 vrf vrf1
+ no bgp graceful-shutdown
+ """
+ )
+
+ # R1 and R3 should see no change to their routes
+ _, result = _run_cmd_and_check(
+ r1, "show ip bgp 13.1.1.1/32 json", "r1/bgp_route_1.json"
+ )
+ assertmsg = "R1: Route 13.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ _, result = _run_cmd_and_check(
+ r3, "show ip bgp 11.1.1.1/32 json", "r3/bgp_route_1.json"
+ )
+ assertmsg = "R3: Route 11.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ # R5 should see routes from R2 with original attributes.
+ _, result = _run_cmd_and_check(
+ r5, "show ip bgp 14.1.1.1/32 json", "r5/bgp_route_1.json"
+ )
+ assertmsg = "R5: Route 14.1.1.1/32 not present or has unexpected params"
+ assert result is None, assertmsg
+
+ logger.info(
+ "\nTest #5: Successful, routes have their original attributes with default LPREF and without GSHUT"
+ )
+
+ # tgen.mininet_cli()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json
new file mode 100644
index 0000000..dcd5af8
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/ebgp_gshut_topo1.json
@@ -0,0 +1,221 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.1.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"100.0.10.1/32",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ },
+ {
+ "network":"1::1/128",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json
new file mode 100644
index 0000000..464e362
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/ibgp_gshut_topo1.json
@@ -0,0 +1,221 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.1.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes":[
+ {
+ "network":"100.0.10.1/32",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ },
+ {
+ "network":"1::1/128",
+ "no_of_ip":5,
+ "next_hop":"Null0"
+ }]
+ },
+ "r2": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
new file mode 100644
index 0000000..58133c4
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ebgp_gshut_topo1.py
@@ -0,0 +1,606 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 ecmp functionality on BGP GSHUT.
+1. Verify graceful-shutdown functionality with eBGP peers
+2. Verify graceful-shutdown functionality when daemons
+ bgpd/zebra/staticd and frr services are restarted with eBGP peers
+"""
+
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ check_address_types,
+ reset_config_on_routers,
+ step,
+ get_frr_ipv6_linklocal,
+ kill_router_daemons,
+ start_router_daemons,
+ stop_router,
+ start_router,
+ create_route_maps,
+ create_bgp_community_lists,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.2.1", "ipv6": "fd00:0:0:1::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.4.2", "ipv6": "fd00:0:0:3::2"}
+PREFERRED_NEXT_HOP = "link_local"
+BGP_CONVERGENCE = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/ebgp_gshut_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+###########################
+# Local APIs
+###########################
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+###########################
+# TESTCASES
+###########################
+
+
+def test_verify_graceful_shutdown_functionality_with_eBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality with eBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R3 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [{"set": {"locPrf": 0}}],
+ }
+ }
+ }
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_restarting_zebra_bgpd_staticd_frr_with_eBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality when daemons bgpd/zebra/staticd and
+ frr services are restarted with eBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R3 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Restart daemons and frr services")
+
+ for daemon in ["bgpd", "zebra", "staticd", "frr"]:
+ if daemon != "frr":
+ kill_router_daemons(tgen, "r3", ["staticd"])
+ start_router_daemons(tgen, "r3", ["staticd"])
+ else:
+ stop_router(tgen, "r3")
+ start_router(tgen, "r3")
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify BGP routes on R3:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r3", "r1", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r3", "r4", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop2)
+ assert result is True, "Test case {} : 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/bgp_gshut_topo1/test_ibgp_gshut_topo1.py b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
new file mode 100644
index 0000000..a79ce0e
--- /dev/null
+++ b/tests/topotests/bgp_gshut_topo1/test_ibgp_gshut_topo1.py
@@ -0,0 +1,653 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 ecmp functionality on BGP GSHUT.
+1. Verify graceful-shutdown functionality with iBGP peers
+2. Verify graceful-shutdown functionality after
+ deleting/re-adding route-map with iBGP peers
+"""
+
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ check_address_types,
+ reset_config_on_routers,
+ step,
+ get_frr_ipv6_linklocal,
+ create_route_maps,
+ create_bgp_community_lists,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NETWORK = {"ipv4": "100.0.10.1/32", "ipv6": "1::1/128"}
+NEXT_HOP_IP_1 = {"ipv4": "10.0.3.1", "ipv6": "fd00:0:0:3::1"}
+NEXT_HOP_IP_2 = {"ipv4": "10.0.4.1", "ipv6": "fd00:0:0:2::1"}
+PREFERRED_NEXT_HOP = "link_local"
+BGP_CONVERGENCE = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.16")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/ibgp_gshut_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ 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("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+###########################
+# Local APIs
+###########################
+
+
+def next_hop_per_address_family(
+ tgen, dut, peer, addr_type, next_hop_dict, preferred_next_hop=PREFERRED_NEXT_HOP
+):
+ """
+ This function returns link_local or global next_hop per address-family
+ """
+
+ intferface = topo["routers"][peer]["links"]["{}".format(dut)]["interface"]
+ if addr_type == "ipv6" and "link_local" in preferred_next_hop:
+ next_hop = get_frr_ipv6_linklocal(tgen, peer, intf=intferface)
+ else:
+ next_hop = next_hop_dict[addr_type]
+
+ return next_hop
+
+
+###########################
+# TESTCASES
+###########################
+
+
+def test_verify_graceful_shutdown_functionality_with_iBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality with iBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R4 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R4 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [{"set": {"locPrf": 0}}],
+ }
+ }
+ }
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_deleting_re_adding_route_map_with_iBGP_peers_p0(request):
+ """
+ Verify graceful-shutdown functionality after deleting/re-adding route-map
+ with iBGP peers
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ reset_config_on_routers(tgen)
+
+ step("Done in base config: Configure base config as per the topology")
+ step("Base config should be up, verify using BGP convergence")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Done in base config: Advertise prefixes from R1")
+ step("Verify BGP routes are received at R4 with best path from R3 to R1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R1 configure:")
+ step("Create standard bgp community-list to permit graceful-shutdown:")
+ input_dict_1 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "GSHUT",
+ "value": "graceful-shutdown",
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route-map to set community GSHUT in OUT direction")
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"community": {"num": "graceful-shutdown"}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "GSHUT-OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "FRR is setting local-pref to 0 by-default on receiver GSHUT community, "
+ "below step is not needed, but keeping for reference"
+ )
+ step(
+ "On R3, apply route-map IN direction to match GSHUT community "
+ "and set local-preference to 0."
+ )
+
+ step(
+ "Verify BGP convergence on R4 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [{"set": {"locPrf": 0}}],
+ }
+ }
+ }
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete route-map from R1")
+ del_rmap_dict = {
+ "r1": {
+ "route_maps": {
+ "GSHUT-OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "community": {"num": "graceful-shutdown", "delete": True}
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, del_rmap_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP convergence on R3 and ensure that all neighbor state "
+ "is established"
+ )
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("Ensure that best path is selected from R1->R3")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1])
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=[next_hop1])
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Re-add route-map in R1")
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify BGP convergence on R3 and ensure all the neighbours state "
+ "is established"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP routes on R4:")
+ step("local pref for routes coming from R1 is set to 0.")
+
+ for addr_type in ADDR_TYPES:
+ rmap_dict = {"r1": {"route_maps": {"GSHUT-OUT": [{"set": {"locPrf": 0}}]}}}
+
+ static_routes = [NETWORK[addr_type]]
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, static_routes, "GSHUT-OUT", rmap_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Ensure that best path is selected from R4 to R3.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ next_hop1 = next_hop_per_address_family(
+ tgen, "r4", "r2", addr_type, NEXT_HOP_IP_1
+ )
+ next_hop2 = next_hop_per_address_family(
+ tgen, "r4", "r3", addr_type, NEXT_HOP_IP_2
+ )
+
+ input_topo = {key: topo["routers"][key] for key in ["r1"]}
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_topo, next_hop=[next_hop1, next_hop2]
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_topo, next_hop=next_hop1)
+ assert result is True, "Test case {} : 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/bgp_instance_del_test/__init__.py b/tests/topotests/bgp_instance_del_test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/__init__.py
diff --git a/tests/topotests/bgp_instance_del_test/ce1 b/tests/topotests/bgp_instance_del_test/ce1
new file mode 120000
index 0000000..0924eb5
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/ce1
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/ce1 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/ce2 b/tests/topotests/bgp_instance_del_test/ce2
new file mode 120000
index 0000000..8c7a677
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/ce2
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/ce2 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/ce3 b/tests/topotests/bgp_instance_del_test/ce3
new file mode 120000
index 0000000..0abb8e5
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/ce3
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/ce3 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/ce4 b/tests/topotests/bgp_instance_del_test/ce4
new file mode 120000
index 0000000..ddee1ef
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/ce4
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/ce4 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/customize.py b/tests/topotests/bgp_instance_del_test/customize.py
new file mode 120000
index 0000000..99fcf39
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/customize.py
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/customize.py \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/r1 b/tests/topotests/bgp_instance_del_test/r1
new file mode 120000
index 0000000..16babfa
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/r1
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/r1 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/r2 b/tests/topotests/bgp_instance_del_test/r2
new file mode 120000
index 0000000..e25b932
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/r2
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/r2 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/r3 b/tests/topotests/bgp_instance_del_test/r3
new file mode 120000
index 0000000..0d7c189
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/r3
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/r3 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/r4 b/tests/topotests/bgp_instance_del_test/r4
new file mode 120000
index 0000000..2d667d3
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/r4
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/r4 \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/scripts b/tests/topotests/bgp_instance_del_test/scripts
new file mode 120000
index 0000000..c46bf1f
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/scripts
@@ -0,0 +1 @@
+../bgp_l3vpn_to_bgp_vrf/scripts \ No newline at end of file
diff --git a/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py
new file mode 100755
index 0000000..0c7e84a
--- /dev/null
+++ b/tests/topotests/bgp_instance_del_test/test_bgp_instance_del_test.py
@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import sys
+import pytest
+
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
+
+from lib.ltemplate import *
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ldpd, pytest.mark.ospfd]
+
+
+def test_check_linux_vrf():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc)
+
+
+def test_adjacencies():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc)
+
+
+def SKIP_test_add_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc)
+
+
+# manual data path setup test - remove once have bgp/zebra vrf path working
+def test_check_linux_mpls():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc)
+
+
+def test_del_bgp_instances():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/del_bgp_instances.py", False, CliOnFail, CheckFunc)
+
+
+if __name__ == "__main__":
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/__init__.py b/tests/topotests/bgp_ipv4_class_e_peer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/__init__.py
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf
new file mode 100644
index 0000000..bf0a68e
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/bgpd.conf
@@ -0,0 +1,12 @@
+!
+allow-reserved-ranges
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 240.0.0.2 remote-as external
+ neighbor 240.0.0.2 timers 1 3
+ neighbor 240.0.0.2 timers connect 1
+ address-family ipv4
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf
new file mode 100644
index 0000000..d4ac46f
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/r1/zebra.conf
@@ -0,0 +1,11 @@
+!
+allow-reserved-ranges
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface r1-eth0
+ ip address 240.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf
new file mode 100644
index 0000000..7d08963
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/bgpd.conf
@@ -0,0 +1,9 @@
+!
+allow-reserved-ranges
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 240.0.0.1 remote-as external
+ neighbor 240.0.0.1 timers 1 3
+ neighbor 240.0.0.1 timers connect 1
+!
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf
new file mode 100644
index 0000000..f0a350f
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/r2/zebra.conf
@@ -0,0 +1,8 @@
+!
+allow-reserved-ranges
+!
+interface r2-eth0
+ ip address 240.0.0.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py
new file mode 100644
index 0000000..c2f0d2e
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_class_e_peer/test_bgp_ipv4_class_e_peer.py
@@ -0,0 +1,127 @@
+#!/usr/bin/env python
+
+#
+# bgp_ipv4_class_e_peer.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+Check if the peering works by using IPv4 Class E IP ranges, and if
+we don't treat next-hop as martian in such a case.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_ipv4_class_e_peer():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge():
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 240.0.0.1 json"))
+ expected = {
+ "240.0.0.1": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_next_hop_ipv4_class_e():
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast 172.16.255.1/32 json")
+ )
+ expected = {
+ "paths": [
+ {
+ "valid": True,
+ "nexthops": [
+ {
+ "ip": "240.0.0.1",
+ "accessible": True,
+ }
+ ],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Initial BGP converge")
+ test_func = functools.partial(_bgp_converge)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see BGP convergence on R2"
+
+ step("Check if IPv4 BGP peering works with Class E IP ranges")
+ test_func = functools.partial(_bgp_next_hop_ipv4_class_e)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to see 172.16.255.1/32 via 240.0.0.1 on R2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json
new file mode 100644
index 0000000..7f928b9
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_ibgp_nbr.json
@@ -0,0 +1,85 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop"
+ }
+ }
+ }
+ }
+ }
+ }
+ }}},
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "200",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop"
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}}}
+ }
+ }
+ }}},
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "200",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }}},
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}}
+ }}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json
new file mode 100644
index 0000000..8e0f448
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_nbr.json
@@ -0,0 +1,95 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ }
+ }}},
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "200",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}}}
+ }
+ }}}},
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "300",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }}},
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+ }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json
new file mode 100644
index 0000000..72d3a93
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ebgp_unnumbered_nbr.json
@@ -0,0 +1,97 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link0": {"ipv4": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ "neighbor_type": "unnumbered"
+ }
+ }
+ }
+ }
+ }
+ }
+ }}},
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link0": {"ipv4": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "200",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ "neighbor_type": "unnumbered"
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}}}
+ }
+ }}}},
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "300",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }}},
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+ }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json
new file mode 100644
index 0000000..a7ea0c8
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_nbr.json
@@ -0,0 +1,95 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r1-link5": {"ipv4": "auto", "ipv6": "auto"}}
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link3": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r0-link5": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link0": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ }
+ }}},
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link0": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}}}
+ }
+ }}}},
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "300",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }}},
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+ }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json
new file mode 100644
index 0000000..5e90d6b
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/rfc5549_ibgp_unnumbered_nbr.json
@@ -0,0 +1,97 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "ipv6": "fd00::", "v6mask": 64},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32, "ipv6": "2001:DB8:F::", "v6mask": 128},
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link1": {"ipv4": "auto"},
+ "r1-link2": {"ipv4": "auto"},
+ "r1-link3": {"ipv4": "auto"},
+ "r1-link4": {"ipv4": "auto"},
+ "r1-link5": {"ipv4": "auto"}}
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r0-link1": {"ipv4": "auto"},
+ "r0-link2": {"ipv4": "auto"},
+ "r0-link3": {"ipv4": "auto"},
+ "r0-link4": {"ipv4": "auto"},
+ "r0-link5": {"ipv4": "auto"},
+ "r2-link0": {"ipv4": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "neighbor_type": "unnumbered",
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ }
+ }
+ }
+ }
+ }}},
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1-link0": {"ipv4": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4": {"ipv4": "auto"}},
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {"dest_link": {"r2": {"activate": "ipv4"}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "neighbor_type": "unnumbered",
+ "capability": "extended-nexthop",
+ "activate": "ipv4"
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}}}
+ }
+ }}}},
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto", "ipv6": "auto"}},
+ "bgp": {
+ "local_as": "300",
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }}},
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2": {"ipv4": "auto"}},
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r4": {}}}}}}
+ }}}}}
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py
new file mode 100644
index 0000000..9f01287
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_ibgp_nbr.py
@@ -0,0 +1,963 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+
+# 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, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ get_frr_ipv6_linklocal,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ reset_config_on_routers,
+ step,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+# Global variables
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+ADDR_TYPES = check_address_types()
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+
+ +----+
+ | R4 |
+ | |
+ +--+-+
+ | ipv4 nbr
+ no bgp ebgp/ibgp |
+ | ebgp/ibgp
+ +----+ 5links +----+ 8links +--+-+ +----+
+ |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 |
+ | +----------+ +------------+ +-------------+ |
+ +----+ +----+ ipv6 nbr +----+ +----+
+"""
+
+TESTCASES = """
+1. Verify Ipv4 route next hop is changed when advertised using
+next hop -self command
+2. Verify IPv4 route advertised to peer when IPv6 BGP session established
+ using peer-group
+3. Verify IPv4 routes received with IPv6 nexthop are getting advertised
+ to another IBGP peer without changing the nexthop
+4. Verify IPv4 routes advertised with correct nexthop when nexthop
+unchange is configure on EBGP peers
+ """
+
+
+def setup_module(mod):
+ """Set up the pytest environment."""
+
+ 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...
+ json_file = "{}/rfc5549_ebgp_ibgp_nbr.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+def test_ibgp_to_ibgp_p1(request):
+ """
+
+ Test Capability extended nexthop.
+
+ Verify IPv4 routes received with IPv6 nexthop are getting advertised to
+ another IBGP peer without changing the nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ global topo
+ topo23 = deepcopy(topo)
+ build_config_from_json(tgen, topo23, save_bkup=False)
+
+ step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address")
+ step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address")
+ step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+ step("Activate same IPv6 nbr from IPv4 unicast family")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+
+ # verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.
+ bgp_convergence = verify_bgp_convergence(tgen, topo23)
+ assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo23, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+
+ gllip = get_llip("r1", "r2-link0")
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 routes installed on R3 with global address without "
+ "changing the nexthop ( nexthop should IPv6 link local which is"
+ " received from R1)"
+ )
+ gipv6 = get_glipv6("r1", "r2-link0")
+ dut = "r3"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gipv6,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gipv6
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ write_test_footer(tc_name)
+
+
+def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request):
+ """
+
+ Test Extended capability next hop, with ibgp peer.
+
+ Verify IPv4 routes advertise using "redistribute static" and
+ "network command" are received on EBGP peer with IPv6 nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step(
+ " Configure IPv6 EBGP session between R1 & R2 with global IPv6 address"
+ " Enable capability extended-nexthop on the nbr from both the routers"
+ " Activate same IPv6 nbr from IPv4 unicast family"
+ )
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4",
+ "next_hop_self": True,
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ gllip = get_llip("r1", "r2-link0")
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ gllip = get_glipv6("r2", "r3")
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_peer_group_p1(request):
+ """
+ Test extended capability next hop with peer groups.
+
+ Verify IPv4 routes received with IPv6 nexthop are getting advertised to
+ another IBGP peer without changing the nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ global topo
+ topo1 = deepcopy(topo)
+ step("Configure IPv6 EBGP session between R1 and R2 with " "global IPv6 address")
+ step("Configure IPv6 IBGP session betn R2 & R3 using IPv6 global address")
+ step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+ step("Activate same IPv6 nbr from IPv4 unicast family")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "peer-group": {
+ "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"}
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ "peer-group": "rfc5549",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "peer-group": {
+ "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"}
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4",
+ "peer-group": "rfc5549",
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}},
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+
+ gllip = get_llip("r1", "r2-link0")
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "peer-group": {
+ "rfc5549": {"capability": "extended-nexthop", "remote-as": "200"}
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ "peer-group": "rfc5549",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "default_ipv4_unicast": "False",
+ "peer-group": {
+ "rfc5549": {"capability": "extended-nexthop", "remote-as": "100"}
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "capability": "extended-nexthop",
+ "activate": "ipv4",
+ "peer-group": "rfc5549",
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2": {}}},
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"neighbor": {"r2": {"dest_link": {"r3": {}}}}}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ step(" Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+
+ gllip = get_llip("r1", "r2-link0")
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ 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/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py
new file mode 100644
index 0000000..48f308e
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_nbr.py
@@ -0,0 +1,630 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ get_frr_ipv6_linklocal,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ reset_config_on_routers,
+ step,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+# Global variables
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+ADDR_TYPES = check_address_types()
+BGP_CONVERGENCE_TIMEOUT = 10
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+ +----+
+ | R4 |
+ | |
+ +--+-+
+ | ipv4 nbr
+ no bgp ebgp |
+ | ebgp/ibgp
+ +----+ 5links +----+ +--+-+ +----+
+ |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 |
+ | +----------+ +------------+ +-------------+ |
+ +----+ +----+ ipv6 nbr +----+ +----+
+"""
+
+TESTCASES = """
+TC6. Verify BGP speaker advertise IPv4 route to peer only if "extended
+ nexthop capability" is negotiated
+TC7. Verify ipv4 route nexthop updated dynamically when in route-map is
+ applied on receiving BGP peer
+TC8. Verify IPv4 routes advertise using "redistribute static" and "network
+ command" are received on EBGP peer with IPv6 nexthop
+TC10. Verify IPv4 routes are deleted after un-configuring of "network
+command" and "redistribute static knob"
+TC18. Verify IPv4 routes installed with correct nexthop after deactivate
+ and activate neighbor from address family
+TC19. Verify IPv4 route ping is working fine and nexhop installed in kernel
+ as IPv4 link-local address
+TC24. Verify IPv4 prefix-list routes advertised to peer when prefix -list
+ applied in out direction
+TC27. Verify IPv4 routes are intact after BGPd process restart
+TC30. Verify Ipv4 route installed with correct next hop when same route
+ is advertised via IPV4 and IPv6 BGP peers
+TC32. Verify IPv4 route received with IPv6 nexthop can be advertised to
+ another IPv4 BGP peers
+ """
+
+
+def setup_module(mod):
+ """Set up the pytest environment."""
+ global topo, ADDR_TYPES
+
+ 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...
+ json_file = "{}/rfc5549_ebgp_nbr.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+ 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()
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ebgp_peer_tc8_p0(request):
+ """
+
+ Test exted capability nexthop with route map in.
+
+ Verify IPv4 routes advertise using "redistribute static" and
+ "network command" are received on EBGP peer with IPv6 nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ step("Configure IPv6 EBGP session between R1 and R2 with global" " IPv6 address")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Enable capability extended-nexthop on the nbr from both the "
+ " routers Activate same IPv6 nbr from IPv4 unicast family"
+ )
+ step(
+ " Configure 2 IPv4 static "
+ "routes on R1 (nexthop for static route exists on different "
+ "link of R0"
+ )
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv6"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "True",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ },
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ glip = get_llip("r1", "r2-link0")
+ assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 and IPv6 routes advertised using static and network command "
+ "are received on R2 BGP & routing table , verify using show ip bgp "
+ "show ip route for IPv4 routes and show bgp ipv6,show ipv6 routes "
+ "for IPv6 routes ."
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type][0],
+ "no_of_ip": 2,
+ "next_hop": glip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, addr_type, dut, verify_nh_for_static_rtes, next_hop=glip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_rib
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ verify_nh_for_static_rtes,
+ next_hop=glip,
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify IPv4 routes are installed with IPv6 global nexthop of R1"
+ " R1 to R2 connected link"
+ )
+
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": glip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+
+def test_ext_nh_cap_remove_red_static_network_ebgp_peer_tc10_p1(request):
+ """
+
+ Test exted capability nexthop with route map in.
+
+ Verify IPv4 routes are deleted after un-configuring of
+ network command and redistribute static knob
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ step(
+ "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+ " address Enable capability extended-nexthop on the nbr from both"
+ " the routers , Activate same IPv6 nbr from IPv4 unicast family"
+ )
+ step(
+ " Configure 2 IPv4 static routes "
+ " on R1 nexthop for static route exists on different link of R0"
+ )
+ reset_config_on_routers(tgen)
+
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 unicast"
+ " family respectively from R1. Configure loopback on R1 with IPv4 "
+ "address Advertise loobak from IPv4 unicast family using network "
+ "command from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "local_as": "100",
+ "default_ipv4_unicast": "True",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 and IPv6 routes advertised using static and network command are"
+ " received on R2 BGP and routing table , verify using show ip bgp"
+ " show ip route for IPv4 routes and show bgp, show ipv6 routes"
+ " for IPv6 routes ."
+ )
+
+ glipv6 = get_llip("r1", "r2-link0")
+ assert glipv6 is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "advertise_networks": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": get_glipv6,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=get_glipv6
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ verify_nh_for_static_rtes,
+ next_hop=get_glipv6,
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 routes are installed with IPv6 global nexthop of R1 "
+ " R1 to R2 connected link"
+ )
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "advertise_networks": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": glipv6,
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": glipv6,
+ }
+ ]
+ }
+ }
+
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glipv6, expected=False
+ )
+ assert (
+ bgp_rib is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in BGP rib".format(
+ tc_name
+ )
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ verify_nh_for_static_rtes,
+ next_hop=glipv6,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes " "still present in RIB".format(tc_name)
+
+ step(
+ "After removing IPv4 routes from redistribute static those routes"
+ " are removed from R2, after re-advertising routes which are "
+ " advertised using network are still present in the on R2 with "
+ " IPv6 global nexthop, verify using show ip bgp and show ip routes"
+ )
+
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": glipv6,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glipv6, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_network": 1,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ verify_nh_for_nw_cmd_rtes,
+ next_hop=glipv6,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Error: Routes still present in BGP rib".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
new file mode 100644
index 0000000..c070904
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ebgp_unnumbered_nbr.py
@@ -0,0 +1,779 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import pytest
+import functools
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+from lib.common_config import (
+ write_test_header,
+ start_topology,
+ write_test_footer,
+ start_router,
+ stop_router,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ reset_config_on_routers,
+ step,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import create_router_bgp, verify_bgp_convergence, verify_bgp_rib
+
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+# Global variables
+NO_OF_RTES = 2
+NETWORK_CMD_IP = "1.0.1.17/32"
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+INTF_LIST = [
+ "r2-link0",
+ "r2-link1",
+ "r2-link2",
+ "r2-link3",
+ "r2-link4",
+ "r2-link5",
+ "r2-link6",
+ "r2-link7",
+]
+ADDR_TYPES = check_address_types()
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+
+ +----+
+ | R4 |
+ | |
+ +--+-+
+ | ipv4 nbr
+ no bgp ebgp/ibgp |
+ | ebgp/ibgp
+ +----+ 5links +----+ 8links +--+-+ +----+
+ |R0 +----------+ R1 +------------+ R2 | ipv6 nbr |R3 |
+ | +----------+ +------------+ +-------------+ |
+ +----+ +----+ ipv6 nbr +----+ +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 routes are advertised when IPv6 EBGP loopback session
+ established using Unnumbered interface
+2. Verify IPv4 routes are installed with correct nexthop after
+shut / no shut of nexthop and BGP peer interfaces
+3. Verify IPv4 routes are intact after stop and start the FRR services
+ """
+
+
+def setup_module(mod):
+ """Set up the pytest environment."""
+ global topo, ADDR_TYPES
+
+ 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...
+ json_file = "{}/rfc5549_ebgp_unnumbered_nbr.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+ 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()
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_unnumbered_loopback_ebgp_nbr_p0(request):
+ """
+
+ Test extended capability nexthop with un numbered ebgp.
+
+ Verify IPv4 routes are advertised when IPv6 EBGP loopback
+ session established using Unnumbered interface
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+
+ step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
+ step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+ step("Activate same IPv6 nbr from IPv4 unicast family")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ },
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+ llip = get_llip("r1", "r2-link0")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+
+ dut = "r2"
+ protocol = "bgp"
+ for rte in range(0, NO_OF_RTES):
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][rte], "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+ """ interface_list = ['r1-link0','r1-link1']
+ nh_list =[]
+ for i in range(NO_OF_RTES):
+ nh_list.append(topo['routers']['r2']['links'][i][
+ 'interface']) """
+ bgp_rib = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ # verify_nh_for_static_rtes, next_hop='r2-r1-eth0')
+ verify_nh_for_static_rtes,
+ next_hop=llip,
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_rib
+ )
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ verify_nh_for_static_rtes,
+ next_hop=llip,
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # verify the routes with nh as ext_nh
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+
+ bgp_rib = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0')
+ verify_nh_for_nw_rtes,
+ next_hop=llip,
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r1")
+ stop_router(tgen, "r2")
+ start_router(tgen, "r1")
+ start_router(tgen, "r2")
+ step(
+ "After stop/start of FRR services , verify session up and routes "
+ "came up fine ,nh is proper using show bgp & show ipv6 route on R2 "
+ )
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ llip = get_llip("r1", "r2-link0")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": llip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen,
+ "ipv4",
+ dut,
+ # verify_nh_for_static_rtes, next_hop='r2-r1-eth0')
+ verify_nh_for_static_rtes,
+ next_hop=llip,
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+ bgp_rib = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ # verify_nh_for_nw_rtes, next_hop='r2-r1-eth0')
+ verify_nh_for_nw_rtes,
+ next_hop=llip,
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ write_test_footer(tc_name)
+
+
+def test_restart_frr_p2(request):
+ """
+
+ Test extended capability nexthop , restart frr.
+
+ Verify IPv4 routes are intact after stop and start the FRR services
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
+ step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+ step("Activate same IPv6 nbr from IPv4 unicast family")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ },
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+
+ llip = get_llip("r1", "r2-link0")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": llip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+
+ bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r1")
+ stop_router(tgen, "r2")
+ start_router(tgen, "r1")
+ start_router(tgen, "r2")
+
+ step(
+ "After stop/start of FRR services , verify session up and routes "
+ "came up fine ,nh is proper using show bgp & show ipv6 route on R2 "
+ )
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ llip = get_llip("r1", "r2-link0")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+ bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # verify the routes with nh as ext_nh
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": llip}
+ ]
+ }
+ }
+ bgp_rib = verify_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ write_test_footer(tc_name)
+
+
+def test_configure_gua_on_unnumbered_intf(request):
+ """
+ Configure a global V6 address on an unnumbered interface on R1
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+
+ step("Configure IPv6 EBGP Unnumbered session between R1 and R2")
+ step("Enable capability extended-nexthop on both the IPv6 BGP peers")
+ step("Activate same IPv6 nbr from IPv4 unicast family")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ step("Verify bgp convergence as ipv6 nbr is enabled on ipv4 addr family.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(" Configure 5 IPv4 static" " routes on R1, Nexthop as different links of R0")
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ },
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ r2 = tgen.gears["r2"]
+
+ def bgp_prefix_received_gua_nh(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json"))
+ expected = {
+ "prefix": "11.0.20.1/32",
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "ip": "5001:dead:beef::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "scope": "global",
+ }
+ ]
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ def bgp_prefix_received_v4_mapped_v6_nh(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast 11.0.20.1/32 json"))
+ expected = {
+ "prefix": "11.0.20.1/32",
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "ip": "::ffff:a00:501",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "scope": "global",
+ }
+ ]
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Configure a global V6 address on an unnumbered interface on R1")
+ output = tgen.gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ interface r1-r2-eth5
+ ipv6 address 5001:dead:beef::1/126
+ !
+ """
+ )
+
+ # verify that r2 has received prefix with GUA as nexthop
+ test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
+ is not 5001:dead:beef::1".format(
+ tc_name
+ )
+
+ step("Configure a secondary global V6 address on an unnumbered interface on R1")
+ output = tgen.gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ interface r1-r2-eth5
+ ipv6 address 7771:dead:beef::1/126
+ !
+ """
+ )
+ # verify that r1 did not readvertise the prefix with secondary V6 address as the nexthop
+ test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
+ is not 5001:dead:beef::1".format(
+ tc_name
+ )
+
+ step("Unconfigure the secondary global V6 address from unnumbered interface on R1")
+ output = tgen.gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ interface r1-r2-eth5
+ no ipv6 address 7771:dead:beef::1/126
+ !
+ """
+ )
+ # verify that r1 still has the prefix with primary GUA as the nexthop
+ test_func = functools.partial(bgp_prefix_received_gua_nh, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
+ is not 5001:dead:beef::1".format(
+ tc_name
+ )
+
+ step("Unconfigure the primary global V6 address from unnumbered interface on R1")
+ output = tgen.gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ interface r1-r2-eth5
+ no ipv6 address 5001:dead:beef::1/126
+ !
+ """
+ )
+ # verify that r1 has rcvd the prefix with v4-mapped-v6 address as the nexthop
+ test_func = functools.partial(bgp_prefix_received_v4_mapped_v6_nh, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Testcase {} : Failed \n Error: Nexthop for prefix 11.0.20.1 \
+ is not ::ffff:a00:501".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py
new file mode 100644
index 0000000..a9e6d21
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_nbr.py
@@ -0,0 +1,988 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+
+# 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, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ addKernelRoute,
+ write_test_footer,
+ create_prefix_lists,
+ verify_rib,
+ create_static_routes,
+ reset_config_on_routers,
+ step,
+ create_route_maps,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+# Global variables
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+NETWORK_CMD_IP = "1.0.1.17/32"
+NO_OF_RTES = 2
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+
+ +----+
+ | R4 |
+ | |
+ +--+-+
+ | ipv4 nbr
+ no bgp ebgp/ibgp |
+ | ebgp/ibgp
+ +----+ 5links +----+ +--+-+ +----+
+ |R0 +----------+ R1 | | R2 | ipv6 nbr |R3 |
+ | +----------+ +------------+ +-------------+ |
+ +----+ +----+ ipv6 nbr +----+ +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 and IPv6 routes advertise using "redistribute static"
+ and "network command" are received on IBGP peer with IPv6 nexthop
+2. Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP session
+ established using loopback interface
+3. Verify IPv4 routes are advertised to peer when static routes are
+ configured with ADMIN distance and tag option
+4. Verify IPv4 routes advertised to peer when BGP session established
+ using link-local address
+ """
+
+
+def setup_module(mod):
+ """Set up the pytest environment."""
+
+ 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...
+ json_file = "{}/rfc5549_ibgp_nbr.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+ 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()
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ibgp_peer_p1(request):
+ """
+
+ Test extended capability nexthop with ibgp peer.
+
+ Verify IPv4 and IPv6 routes advertise using "redistribute static"
+ and "network command" are received on IBGP peer with IPv6 nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step(
+ "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+ " address Enable capability extended-nexthop on the nbr from both"
+ " the routers"
+ )
+ step(
+ "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6"
+ " nbr from IPv4 unicast family "
+ )
+
+ step(
+ " Configure 5 IPv4 static routes"
+ " on R1 nexthop for static route exists on different link of R0"
+ )
+
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 unicast"
+ " family respectively from R1.Configure loopback on R1 with IPv4 addr"
+ " & Advertise loopback from IPv4 unicast family using network cmd "
+ " from R1"
+ )
+ # this test case needs ipv6 routes to be configured
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ },
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ glip = get_llip("r1", "r2-link0")
+ assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 and IPv6 routes advertised using static & network command are"
+ "received on R2 BGP and routing table , verify using show ip bgp"
+ "show ip route for IPv4 routes and show bgp, show ipv6 routes"
+ "for IPv6 routes ."
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": glip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify IPv4 routes are installed with IPv6 global nexthop of R1"
+ "R1 to R2 connected link"
+ )
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": glip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=glip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ext_nh_cap_admin_dist_tag_ibgp_peer_p1(request):
+ """
+
+ Test extended capability nexthop with admin distance and route tag.
+
+ Verify IPv4 routes are advertised to peer when static routes
+ are configured with ADMIN distance and tag option
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step(
+ "Configure IPv6 EBGP session between R1 and R2 with global IPv6"
+ " address Enable capability extended-nexthop on the nbr from both"
+ " the routers"
+ )
+ step(
+ "Change ebgp to ibgp nbrs between r1 and r2 , Activate same IPv6"
+ " nbr from IPv4 unicast family "
+ )
+ step(
+ " Configure 5 IPv4 static routes"
+ " on R1 nexthop for static route exists on different link of R0"
+ )
+ count = 0
+ for rte in range(0, NO_OF_RTES):
+ count += 1
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ "admin_distance": 100 + count,
+ "tag": 4001 + count,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Advertise static routes from IPv4 unicast family & IPv6 unicast"
+ " family respectively from R1.Configure loopback on R1 with IPv4 "
+ "address & Advertise loopback from IPv4 unicast family "
+ "using network cmd from R1"
+ )
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ glip = get_llip("r1", "r2-link0")
+ assert glip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 and IPv6 routes advertised using static & network cmd are"
+ "received on R2 BGP and routing table , verify using show ip bgp"
+ "show ip route for IPv4 routes and show bgp, show ipv6 routes"
+ "for IPv6 routes ."
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ count = 0
+ # verify the routes with nh as ext_nh
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": glip,
+ "admin_distance": 100 + count,
+ "tag": 4001 + count,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=glip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ count = 0
+ for rte in range(0, NO_OF_RTES):
+ count += 10
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 0 + count,
+ "action": "permit",
+ "network": NETWORK["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_6 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format("ipv4"): [
+ {
+ "action": "deny",
+ "match": {
+ "ipv4": {"prefix_lists": "pf_list_1_{}".format("ipv4")}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ibgp_loopback_nbr_p1(request):
+ """
+ Verify Extended capability nexthop with loopback interface.
+
+ Verify IPv4 routes are advertised and withdrawn when IPv6 IBGP
+ session established using loopback interface
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ global topo
+ topo1 = deepcopy(topo)
+ reset_config_on_routers(tgen)
+ step("Configure IPv6 global address between R1 and R2")
+ step(
+ "Configure loopback on R1 and R2 and establish EBGP session "
+ "between R1 and R2 over loopback global ip"
+ )
+ step("Configure static route on R1 and R2 for loopback reachability")
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+
+ for routerN in ["r1", "r2"]:
+ for addr_type in ["ipv6"]:
+ for bgp_neighbor in topo1["routers"][routerN]["bgp"]["address_family"][
+ addr_type
+ ]["unicast"]["neighbor"].keys():
+ # Adding ['source_link'] = 'lo' key:value pair
+ if bgp_neighbor == "r1" or bgp_neighbor == "r2":
+ topo1["routers"][routerN]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][bgp_neighbor]["dest_link"] = {
+ "lo": {
+ "source_link": "lo",
+ "ebgp_multihop": 2,
+ "capability": "extended-nexthop",
+ "activate": "ipv4",
+ }
+ }
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r1-link0": {"deactivate": "ipv6"}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r2-link0": {"deactivate": "ipv6"}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {"r1-link0": {"deactivate": "ipv4"}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r2-link0": {"deactivate": "ipv4"}}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"]
+ r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"]
+ r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+ r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0]
+ r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0]
+
+ ipv4_list = [("r1", r1_r2_intf, [r2_lo_v4]), ("r2", r2_r1_intf, [r1_lo_v4])]
+
+ ipv6_list = [
+ ("r1", r1_r2_intf, [r2_lo_v6], r2_r1_v6_nh),
+ ("r2", r2_r1_intf, [r1_lo_v6], r1_r2_v6_nh),
+ ]
+
+ for dut, intf, loop_addr in ipv4_list:
+ result = addKernelRoute(tgen, dut, intf, loop_addr)
+ # assert result is True, "Testcase {}:Failed \n Error: {}". \
+ # format(tc_name, result)
+
+ for dut, intf, loop_addr, next_hop in ipv6_list:
+ result = addKernelRoute(tgen, dut, intf, loop_addr, next_hop)
+ # assert result is True, "Testcase {}:Failed \n Error: {}". \
+ # format(tc_name, result)
+
+ r2_lo_v4 = topo["routers"]["r2"]["links"]["lo"]["ipv4"]
+ r2_lo_v6 = topo["routers"]["r2"]["links"]["lo"]["ipv6"]
+ r1_lo_v4 = topo["routers"]["r1"]["links"]["lo"]["ipv4"]
+ r1_lo_v6 = topo["routers"]["r1"]["links"]["lo"]["ipv6"]
+ r1_r2_intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+ r2_r1_intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ r1_r2_v6_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0]
+ r2_r1_v6_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv6"].split("/")[0]
+
+ r1_r2_v4_nh = topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0]
+ r2_r1_v4_nh = topo["routers"]["r2"]["links"]["r1-link0"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": r2_lo_v4, "next_hop": r2_r1_v4_nh},
+ {"network": r2_lo_v6, "next_hop": r2_r1_v6_nh},
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {"network": r1_lo_v4, "next_hop": r1_r2_v4_nh},
+ {"network": r1_lo_v6, "next_hop": r1_r2_v6_nh},
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether BGP is converged
+ result = verify_bgp_convergence(tgen, topo1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Enable cap ext nh on r1 and r2 and activate in ipv4 addr family")
+ configure_bgp_on_r1 = {
+ "r1": {
+ "default_ipv4_unicast": False,
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "lo": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r2 = {
+ "r2": {
+ "default_ipv4_unicast": False,
+ "bgp": {
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "lo": {
+ "activate": "ipv4",
+ "capability": "extended-nexthop",
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify bgp convergence.")
+ bgp_convergence = verify_bgp_convergence(tgen, topo1)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Configure 2 IPv4 static" " routes on R1, Nexthop as different links of R0")
+
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 "
+ "unicast family respectively from R1 using red static cmd "
+ "Advertise loopback from IPv4 unicast family using network command "
+ "from R1"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "IPv4 routes advertised using static and network command are "
+ " received on R2 BGP and routing table , "
+ "verify using show ip bgp, show ip route for IPv4 routes ."
+ )
+
+ gllip = (topo1["routers"]["r1"]["links"]["lo"]["ipv6"].split("/")[0]).lower()
+ assert gllip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": gllip,
+ }
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip
+ )
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Remove IPv4 routes advertised using network command"
+ " from R1 and advertise again"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_network": 1,
+ "delete": True,
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_network": 1,
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "After removing IPv4 routes from network command , routes which are "
+ "advertised using redistribute static are still present in the on "
+ "R2 , verify using show ip bgp and show ip route"
+ )
+
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Remove IPv4 routes advertised using redistribute static"
+ " command from R1 and advertise again"
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo1, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "After removing IPv4 routes from redistribute static , routes which"
+ " are advertised using network are still present in the on R2 , "
+ "verify using show ip bgp and show ip route"
+ )
+
+ verify_nh_for_nw_rtes = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_CMD_IP, "no_of_ip": 1, "next_hop": gllip}
+ ]
+ }
+ }
+ bgp_rib = verify_bgp_rib(tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip)
+ assert bgp_rib is True, "Testcase {} : Failed \n Error: {}".format(tc_name, bgp_rib)
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_rtes, next_hop=gllip, protocol=protocol
+ )
+ 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/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py
new file mode 100644
index 0000000..9a0fc44
--- /dev/null
+++ b/tests/topotests/bgp_ipv4_over_ipv6/test_rfc5549_ibgp_unnumbered_nbr.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""RFC5549 Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ reset_config_on_routers,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.bgp import create_router_bgp, verify_bgp_convergence
+from lib.topojson import build_config_from_json
+
+# Global variables
+topo = None
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK_CMD_IP = "1.0.1.17/32"
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+ADDR_TYPES = check_address_types()
+NO_OF_RTES = 2
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+ +----+
+ | R4 |
+ | |
+ +--+-+
+ | ipv4 nbr
+ no bgp ebgp/ibgp |
+ | ebgp/ibgp
+ +----+ 2links +----+ 8links +--+-+ +----+
+ |R0 +----------+ R1 + + R2 | ipv6 nbr |R3 |
+ | +----------+ +------------+ +-------------+ |
+ +----+ +----+ ipv6 nbr +----+ +----+
+"""
+
+TESTCASES = """
+1. Verify IPv4 routes are deleted after un-configuring "network command
+" and "redistribute static knob" with Unnumbered IPv6 IBGP session
+ """
+
+
+def setup_module(mod):
+ """Set up the pytest environment."""
+
+ 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...
+ json_file = "{}/rfc5549_ibgp_unnumbered_nbr.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ 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()
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ext_nh_cap_red_static_network_ebgp_peer_unnumbered_nbr_p1(request):
+ """
+
+ Test extended capability nexthop.
+
+ Verify IPv4 routes advertise using "redistribute static" and
+ "network command" are received on EBGP peer with IPv6 nexthop
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step(
+ "Configure IPv6 IBGP Unnumbered session between R1 and R2 and enable "
+ "ipv6 nd ra-interval 10 in the interface"
+ )
+
+ step(
+ "Enable capability extended-nexthop"
+ "on the neighbor from both the routers and "
+ "ipv6 nd ra-interval 10 on link connected between R1 and R2"
+ )
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ for rte in range(0, NO_OF_RTES):
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][rte],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP["ipv4"][rte],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Advertise static routes from IPv4 unicast family and IPv6 unicast "
+ "family respectively from R1 "
+ "Configure loopback on R1 with IPv4 address Advertise loopback "
+ "from IPv4 unicast family using network cmd from R1 "
+ )
+
+ configure_bgp_on_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [{"redist_type": "static"}],
+ "advertise_networks": [
+ {"network": NETWORK_CMD_IP, "no_of_network": 1}
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ llip = get_llip("r1", "r2-link0")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ " IPv4 and IPv6 routes advertised using static and network command are"
+ " received on R2 BGP and routing table , verify using show ip bgp"
+ " show ip route for IPv4 routes and show bgp show ipv6 routes"
+ " for IPv6 routes ."
+ )
+
+ dut = "r2"
+ protocol = "bgp"
+ verify_nh_for_static_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": NO_OF_RTES,
+ "next_hop": llip,
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_static_rtes, next_hop=llip, protocol=protocol
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ verify_nh_for_nw_cmd_rtes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK_CMD_IP,
+ "no_of_ip": 1,
+ "next_hop": llip,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, "ipv4", dut, verify_nh_for_nw_cmd_rtes, next_hop=llip, protocol=protocol
+ )
+ 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/bgp_ipv6_rtadv/__init__.py b/tests/topotests/bgp_ipv6_rtadv/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/__init__.py
diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf
new file mode 100644
index 0000000..0918796
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r1/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 101
+ bgp router-id 10.254.254.1
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r1-eth0 interface peer-group r2g
+ neighbor r1-eth0 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json
new file mode 100644
index 0000000..73c7fbb
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv4_routes.json
@@ -0,0 +1,40 @@
+{
+ "10.254.254.2/32": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "10.254.254.2/32",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "10.254.254.1/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json
new file mode 100644
index 0000000..a50bffd
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r1/ipv6_routes.json
@@ -0,0 +1,34 @@
+{
+ "2001:db8:1::/64": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "interfaceName": "r1-eth0",
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ },
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r1-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf
new file mode 100644
index 0000000..1397c7f
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r1/zebra.conf
@@ -0,0 +1,9 @@
+! debug zebra packet recv
+! debug zebra packet send
+log stdout
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf
new file mode 100644
index 0000000..55d4856
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 102
+ bgp router-id 10.254.254.2
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r2-eth0 interface peer-group r2g
+ neighbor r2-eth0 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json
new file mode 100644
index 0000000..d4b1410
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv4_routes.json
@@ -0,0 +1,40 @@
+{
+ "10.254.254.2/32": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "10.254.254.2/32",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "lo",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "distance": 20,
+ "protocol": "bgp",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "10.254.254.1/32",
+ "nexthops": [
+ {
+ "interfaceName": "r2-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true,
+ "afi": "ipv6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json
new file mode 100644
index 0000000..e7bfb99
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r2/ipv6_routes.json
@@ -0,0 +1,21 @@
+{
+ "2001:db8:1::/64": [
+ {
+ "distance": 0,
+ "protocol": "connected",
+ "metric": 0,
+ "selected": true,
+ "destSelected": true,
+ "prefix": "2001:db8:1::/64",
+ "nexthops": [
+ {
+ "directlyConnected": true,
+ "interfaceName": "r2-eth0",
+ "fib": true,
+ "flags": 3,
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf
new file mode 100644
index 0000000..0131a11
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/r2/zebra.conf
@@ -0,0 +1,9 @@
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot
new file mode 100644
index 0000000..da67c29
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.dot
@@ -0,0 +1,44 @@
+## 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-topo2";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+}
diff --git a/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py
new file mode 100644
index 0000000..981028f
--- /dev/null
+++ b/tests/topotests/bgp_ipv6_rtadv/test_bgp_ipv6_rtadv.py
@@ -0,0 +1,148 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_ipv6_rtadv.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_ipv6_rtadv.py: Test the FRR BGP daemon with BGP IPv6 interface
+ with route advertisements on a separate netns.
+"""
+
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv4 routing tables.
+ logger.info("Checking IPv4 routes for convergence")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check IPv6 routing tables.
+ logger.info("Checking IPv6 routes for convergence")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ipv6 route json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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_l3vpn_to_bgp_direct/__init__.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/__init__.py
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
new file mode 100644
index 0000000..8b88534
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/bgpd.conf
@@ -0,0 +1,32 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ no bgp network import-check
+ bgp router-id 99.0.0.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5226
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 5.1.0.0/24 route-map rm-nh
+ network 5.1.1.0/24 route-map rm-nh
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.1
+ set local-preference 123
+ set metric 98
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf
new file mode 100644
index 0000000..46831bb
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface lo
+ ip address 99.0.0.1/32
+!
+interface ce1-eth0
+ description to r1
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
new file mode 100644
index 0000000..2accaae
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/bgpd.conf
@@ -0,0 +1,32 @@
+frr defaults traditional
+!
+hostname ce2
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ no bgp network import-check
+ bgp router-id 99.0.0.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5226
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 5.1.0.0/24 route-map rm-nh
+ network 5.1.1.0/24 route-map rm-nh
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.2
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf
new file mode 100644
index 0000000..fb4d8cc
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce2/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface lo
+ ip address 99.0.0.2/32
+!
+interface ce2-eth0
+ description to r3
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
new file mode 100644
index 0000000..42eff1b
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/bgpd.conf
@@ -0,0 +1,32 @@
+frr defaults traditional
+!
+hostname ce3
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ no bgp network import-check
+ bgp router-id 99.0.0.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5226
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 5.1.2.0/24 route-map rm-nh
+ network 5.1.3.0/24 route-map rm-nh
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.3
+ set local-preference 50
+ set metric 200
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf
new file mode 100644
index 0000000..77a1163
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/ce3/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface lo
+ ip address 99.0.0.3/32
+!
+interface ce3-eth0
+ description to r4
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py
new file mode 100644
index 0000000..c0a5c7a
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/customize.py
@@ -0,0 +1,146 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+r"""
+customize.py: Simple FRR MPLS L3VPN test topology
+
+ |
+ +----+----+
+ | ce1 |
+ | 99.0.0.1| CE Router
+ +----+----+
+ 192.168.1. | .2 ce1-eth0
+ | .1 r1-eth4
+ +---------+
+ | r1 |
+ | 1.1.1.1 | PE Router
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 | P router
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 |
+ | 3.3.3.3 | | 4.4.4.4 | PE Routers
+ +-----------+ +---------+
+ 192.168.1. | .1 192.168.1. | .1 rX-eth4
+ | .2 | .2 ceX-eth0
+ +-----+-----+ +----+-----+
+ | ce2 | | ce3 |
+ | 99.0.0.2 | | 99.0.0.3 | CE Routers
+ +-----+-----+ +----+-----+
+ | |
+
+"""
+
+import os
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import get_topogen
+from lib.topolog import logger
+from lib.ltemplate import ltemplateRtrCmd
+
+# Required to instantiate the topology builder class.
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+# test name based on directory
+TEST = os.path.basename(CWD)
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ # Create P/PE routers
+ tgen.add_router("r1")
+ # check for mpls
+ if tgen.hasmpls != True:
+ logger.info("MPLS not available, tests will be skipped")
+ return
+ for routern in range(2, 5):
+ tgen.add_router("r{}".format(routern))
+ # Create CE routers
+ for routern in range(1, 4):
+ tgen.add_router("ce{}".format(routern))
+
+ # CE/PE links
+ tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4")
+ tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4")
+ tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4")
+
+ # Create a switch with just one router connected to it to simulate a
+ # empty network.
+ switch = {}
+ switch[0] = tgen.add_switch("sw0")
+ switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0")
+ switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0")
+
+ switch[1] = tgen.add_switch("sw1")
+ switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1")
+ switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0")
+ switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0")
+
+ switch[1] = tgen.add_switch("sw2")
+ switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2")
+ switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1")
+
+
+def ltemplatePreRouterStartHook():
+ cc = ltemplateRtrCmd()
+ tgen = get_topogen()
+ logger.info("pre router-start hook")
+ # check for mpls
+ if tgen.hasmpls != True:
+ logger.info("MPLS not available, skipping setup")
+ return False
+ logger.info("setup mpls input")
+ return True
+
+
+def ltemplatePostRouterStartHook():
+ logger.info("post router-start hook")
+ return True
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
new file mode 100644
index 0000000..a564da9
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/bgpd.conf
@@ -0,0 +1,42 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 1.1.1.1
+ bgp cluster-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as 5226
+ neighbor 192.168.1.2 update-source 192.168.1.1
+ neighbor 192.168.1.2 route-reflector-client
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 1.1.1.1
+ neighbor 2.2.2.2 timers 3 10
+!
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ vrf-policy cust1
+ label 101
+ rd 10:1
+ rt both 52:100
+ nexthop 192.168.1.1
+ exit-vrf-policy
+!
+ vnc export bgp mode group-nve
+ vnc export bgp group-nve group cust1
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+ !
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf
new file mode 100644
index 0000000..f7f2714
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ !
+ interface r1-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf
new file mode 100644
index 0000000..460a8fb
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf
new file mode 100644
index 0000000..767e17e
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r1/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ mpls
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to sw0
+ mpls
+ ip address 10.0.1.1/24
+ no link-detect
+!
+interface r1-eth4
+ description to ce1
+ mpls
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.1/32 192.168.1.2
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
new file mode 100644
index 0000000..3167306
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/bgpd.conf
@@ -0,0 +1,33 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 2.2.2.2
+ bgp cluster-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ neighbor 1.1.1.1 remote-as 5226
+ neighbor 1.1.1.1 update-source 2.2.2.2
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 3.3.3.3 remote-as 5226
+ neighbor 3.3.3.3 update-source 2.2.2.2
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 4.4.4.4 remote-as 5226
+ neighbor 4.4.4.4 update-source 2.2.2.2
+ neighbor 4.4.4.4 timers 3 10
+ address-family ipv4 unicast
+ no neighbor 1.1.1.1 activate
+ no neighbor 3.3.3.3 activate
+ no neighbor 4.4.4.4 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 1.1.1.1 activate
+ neighbor 1.1.1.1 route-reflector-client
+ neighbor 3.3.3.3 activate
+ neighbor 3.3.3.3 route-reflector-client
+ neighbor 4.4.4.4 activate
+ neighbor 4.4.4.4 route-reflector-client
+ exit-address-family
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf
new file mode 100644
index 0000000..c4056e0
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf
new file mode 100644
index 0000000..dbed618
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf
new file mode 100644
index 0000000..829ac96
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r2/zebra.conf
@@ -0,0 +1,31 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ mpls
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ mpls
+ ip address 10.0.1.2/24
+ no link-detect
+!
+interface r2-eth1
+ description to sw1
+ mpls
+ ip address 10.0.2.2/24
+ no link-detect
+!
+interface r2-eth2
+ description to sw2
+ mpls
+ ip address 10.0.3.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
new file mode 100644
index 0000000..445da08
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/bgpd.conf
@@ -0,0 +1,41 @@
+frr defaults traditional
+!
+hostname r3
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 3.3.3.3
+ bgp cluster-id 3.3.3.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as 5226
+ neighbor 192.168.1.2 update-source 192.168.1.2
+ neighbor 192.168.1.2 route-reflector-client
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 3.3.3.3
+ neighbor 2.2.2.2 timers 3 10
+!
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ vrf-policy cust1
+ label 103
+ rd 10:3
+ rt both 52:100
+ nexthop 192.168.1.1
+ exit-vrf-policy
+!
+ vnc export bgp mode group-nve
+ vnc export bgp group-nve group cust1
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf
new file mode 100644
index 0000000..48956cb
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf
new file mode 100644
index 0000000..0e64ed6
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf
new file mode 100644
index 0000000..916dabf
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r3/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r3
+!
+interface lo
+ mpls
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ mpls
+ ip address 10.0.2.3/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+ no link-detect
+!
+interface r3-eth4
+ description to ce2
+ mpls
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.2/32 192.168.1.2
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
new file mode 100644
index 0000000..1941352
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/bgpd.conf
@@ -0,0 +1,41 @@
+frr defaults traditional
+!
+hostname r4
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 4.4.4.4
+ bgp cluster-id 4.4.4.4
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as 5226
+ neighbor 192.168.1.2 update-source 192.168.1.1
+ neighbor 192.168.1.2 route-reflector-client
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 4.4.4.4
+ neighbor 2.2.2.2 timers 3 10
+!
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ vrf-policy cust1
+ label 104
+ rd 10:4
+ rt both 52:100
+ nexthop 192.168.1.1
+ exit-vrf-policy
+!
+ vnc export bgp mode group-nve
+ vnc export bgp group-nve group cust1
+ vnc redistribute mode resolve-nve
+ vnc redistribute ipv4 bgp-direct
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf
new file mode 100644
index 0000000..1d04aa0
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r4
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 4.4.4.4
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ !
+ interface r4-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf
new file mode 100644
index 0000000..89e37df
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf
new file mode 100644
index 0000000..e08ac86
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/r4/zebra.conf
@@ -0,0 +1,26 @@
+log file zebra.log
+!
+hostname r4
+!
+interface lo
+ mpls
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ mpls
+ ip address 10.0.2.4/24
+ no link-detect
+!
+interface r4-eth4
+ description to ce3
+ mpls
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.3/32 192.168.1.2
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py
new file mode 100644
index 0000000..0deb181
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/add_routes.py
@@ -0,0 +1,193 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+)
+luCommand(
+ "r4", 'vtysh -c "show bgp next"', "99.0.0.. valid", "wait", "See CE static NH"
+)
+luCommand("r1", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
+luCommand("r3", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
+luCommand("r4", 'vtysh -c "show bgp ipv4 uni"', "i5.*i5", "wait", "See CE routes")
+luCommand("ce1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
+luCommand("r1", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
+luCommand("ce2", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
+luCommand("r3", 'vtysh -c "show bgp ipv4 uni 5.1.0.0/24"', "", "none", "See CE routes")
+luCommand("ce3", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes")
+luCommand("r4", 'vtysh -c "show bgp ipv4 uni 5.1.2.0/24"', "", "none", "See CE routes")
+
+luCommand(
+ "r1", 'vtysh -c "add vrf cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.1",
+ "wait",
+ "Local Registration",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations imported"',
+ "2 out of 2 imported",
+ "wait",
+ "Imported Registrations",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.1/32",
+ "wait",
+ "See R1s static address",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.1/32",
+ "wait",
+ "See R1s static address",
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports"
+)
+luCommand(
+ "r4", 'vtysh -c "show bgp ipv4 vpn rd 10:1"', "i5.*i5", "wait", "See R1s imports"
+)
+
+luCommand(
+ "r3", 'vtysh -c "add vrf cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.2",
+ "wait",
+ "Local Registration",
+)
+have2ndImports = luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations imported"',
+ "2 out of 2 imported",
+ "none",
+ "Imported Registrations",
+ 2,
+)
+if have2ndImports:
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations imported"',
+ "2 out of 2 imported",
+ "pass",
+ "Imported Registrations",
+ )
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.2/32",
+ "wait",
+ "See R3s static address",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.2/32",
+ "wait",
+ "See R3s static address",
+)
+if have2ndImports:
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn rd 10:3"',
+ "i5.*i5",
+ "none",
+ "See R3s imports",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 vpn rd 10:3"',
+ "i5.*i5",
+ "none",
+ "See R3s imports",
+ )
+
+luCommand(
+ "r4", 'vtysh -c "add vrf cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.3",
+ "wait",
+ "Local Registration",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations imported"',
+ "2 out of 2 imported",
+ "wait",
+ "Imported Registrations",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.3/32",
+ "wait",
+ "See R4s static address",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "i99.0.0.3/32",
+ "wait",
+ "See R4s static address",
+)
+luCommand(
+ "r1", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports"
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp ipv4 vpn rd 10:4"', "i5.*i5", "wait", "See R4s imports"
+)
+
+
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations remote"',
+ "5.1.2.0/24 .*5.1.3.0/24",
+ "wait",
+ "R4s registrations",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations remote"',
+ "5.1.2.0/24 .*5.1.3.0/24",
+ "wait",
+ "R4s registrations",
+)
+if have2ndImports:
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations remote"',
+ "5.1.0.0/24 .*5.1.1.0/24",
+ "wait",
+ "Remote registrations",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations remote"',
+ "5.1.0.0/24 .*5.1.1.0/24",
+ "wait",
+ "Remote registrations",
+ )
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations remote"',
+ "5.1.0.0/24 .*5.1.1.0/24",
+ "wait",
+ "Remote registrations",
+)
+luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py
new file mode 100644
index 0000000..6cd92e2
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/adjacencies.py
@@ -0,0 +1,71 @@
+from lib.lutil import luCommand
+
+luCommand("ce1", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping")
+luCommand("ce2", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping")
+luCommand("ce3", "ping 192.168.1.1 -c 1", " 0. packet loss", "pass", "CE->PE ping")
+luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand(
+ "r1", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60)
+luCommand(
+ "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r3", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60)
+luCommand(
+ "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+
+luCommand(
+ "r4", 'vtysh -c "show ip route ospf"', "2.2.2.2", "wait", "OSPF Route has Arrived", 60)
+luCommand(
+ "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp summary"',
+ " 00:0.* 00:0.* 00:0",
+ "wait",
+ "Core adjacencies up",
+ 180,
+)
+luCommand(
+ "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping"
+)
+luCommand(
+ "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping"
+)
+luCommand(
+ "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping"
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py
new file mode 100644
index 0000000..af39a95
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/check_routes.py
@@ -0,0 +1,55 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "7 routes and 7",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "7 routes and 9",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "7 routes and 7",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "r1", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI"
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Unicast SAFI",
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI"
+)
+luCommand(
+ "r4", 'vtysh -c "show bgp ipv4 uni"', "7 routes and 9", "pass", "Unicast SAFI"
+)
+have2ndImports = luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations imported"',
+ "2 out of 2 imported",
+ "none",
+ "Imported Registrations",
+ 2,
+)
+if have2ndImports:
+ num = "9 routes and 9"
+else:
+ num = "7 routes and 7"
+luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI")
+luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI")
+luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI")
+luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI")
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py
new file mode 100644
index 0000000..38eac14
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/scripts/cleanup_all.py
@@ -0,0 +1,114 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1",
+ 'vtysh -c "clear vrf cust1 prefix 99.0.0.1/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "clear vrf cust1 prefix 99.0.0.2/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "clear vrf cust1 prefix 99.0.0.3/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.1",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.2",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.3",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Unicast SAFI",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+)
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py
new file mode 100755
index 0000000..be12cfd
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_direct/test_bgp_l3vpn_to_bgp_direct.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import sys
+import pytest
+
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
+
+from lib.ltemplate import *
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def test_adjacencies():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc)
+
+
+def test_add_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_cleanup_all():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc)
+
+
+if __name__ == "__main__":
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/__init__.py
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
new file mode 100644
index 0000000..ae57431
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -0,0 +1,55 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log
+
+router bgp 5227
+ no bgp network import-check
+ bgp router-id 99.0.0.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5227
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 99.0.0.1/32
+ network 5.1.0.0/24 route-map rm-nh
+ network 5.1.1.0/24 route-map rm-nh
+ redistribute sharp route-map sharp-nh
+ network 6.0.1.0/24 route-map rm-nh
+ network 6.0.2.0/24 route-map rm-nh-same
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.1
+ set local-preference 123
+ set metric 98
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+route-map sharp-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.1
+ set local-preference 200
+ set metric 200
+ set large-community 90:12:34
+ set extcommunity rt 80:987
+ set community 0:65
+!
+route-map rm-nh-same permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.1
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:11
+ set extcommunity rt 89:123
+ set community 0:67
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/sharpd.conf
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf
new file mode 100644
index 0000000..46831bb
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface lo
+ ip address 99.0.0.1/32
+!
+interface ce1-eth0
+ description to r1
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
new file mode 100644
index 0000000..599e2dd
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -0,0 +1,55 @@
+frr defaults traditional
+!
+hostname ce2
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log
+
+router bgp 5227
+ no bgp network import-check
+ bgp router-id 99.0.0.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5227
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 99.0.0.2/32
+ network 5.1.0.0/24 route-map rm-nh
+ network 5.1.1.0/24 route-map rm-nh
+ redistribute sharp route-map sharp-nh
+ network 6.0.1.0/24 route-map rm-nh
+ network 6.0.2.0/24 route-map rm-nh-same
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.2
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+route-map sharp-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.2
+ set local-preference 200
+ set metric 200
+ set large-community 78:90:12
+ set extcommunity rt 70:456
+ set community 0:66
+!
+route-map rm-nh-same permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.2
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:12
+ set extcommunity rt 89:123
+ set community 0:67
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/sharpd.conf
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf
new file mode 100644
index 0000000..fb4d8cc
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce2/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface lo
+ ip address 99.0.0.2/32
+!
+interface ce2-eth0
+ description to r3
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
new file mode 100644
index 0000000..e316de5
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/bgpd.conf
@@ -0,0 +1,45 @@
+frr defaults traditional
+!
+hostname ce3
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log
+
+router bgp 5227
+ no bgp network import-check
+ bgp router-id 99.0.0.3
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 5227
+ neighbor 192.168.1.1 update-source 192.168.1.2
+ neighbor 192.168.1.1 timers 3 10
+ address-family ipv4 unicast
+ network 99.0.0.3/32
+ network 5.1.2.0/24 route-map rm-nh
+ network 5.1.3.0/24 route-map rm-nh
+ network 6.0.1.0/24 route-map rm-nh
+ network 6.0.2.0/24 route-map rm-nh-same
+ neighbor 192.168.1.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.3
+ set local-preference 50
+ set metric 200
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+route-map rm-nh-same permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.3
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:13
+ set extcommunity rt 89:123
+ set community 0:67
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf
new file mode 100644
index 0000000..77a1163
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce3/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface lo
+ ip address 99.0.0.3/32
+!
+interface ce3-eth0
+ description to r4
+ ip address 192.168.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
new file mode 100644
index 0000000..60d9e93
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/bgpd.conf
@@ -0,0 +1,45 @@
+frr defaults traditional
+!
+hostname ce4
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log
+
+router bgp 5228 vrf ce4-cust2
+ no bgp network import-check
+ bgp router-id 99.0.0.4
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as 5228
+ neighbor 192.168.2.1 update-source 192.168.2.2
+ neighbor 192.168.2.1 timers 3 10
+ address-family ipv4 unicast
+ network 99.0.0.4/32
+ network 5.4.2.0/24 route-map rm-nh
+ network 5.4.3.0/24 route-map rm-nh
+ network 6.0.1.0/24 route-map rm-nh
+ network 6.0.2.0/24 route-map rm-nh-same
+ neighbor 192.168.2.1 activate
+ exit-address-family
+!
+access-list al-any permit any
+!
+route-map rm-nh permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.4
+ set local-preference 50
+ set metric 200
+ set large-community 12:34:56
+ set extcommunity rt 89:123
+ set community 0:67
+!
+route-map rm-nh-same permit 10
+ match ip address al-any
+ set ip next-hop 99.0.0.4
+ set local-preference 100
+ set metric 100
+ set large-community 12:34:14
+ set extcommunity rt 89:123
+ set community 0:67
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf
new file mode 100644
index 0000000..e55c9e7
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/ce4/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname ce4
+!
+interface ce4-cust2
+ ip address 99.0.0.4/32
+!
+interface ce4-eth0
+ description to r4
+ ip address 192.168.2.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py
new file mode 100644
index 0000000..5161d84
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/customize.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+r"""
+customize.py: Simple FRR MPLS L3VPN test topology
+
+ |
+ +----+----+
+ | ce1 |
+ | 99.0.0.1| CE Router
+ +----+----+
+ 192.168.1. | .2 ce1-eth0
+ | .1 r1-eth4
+ +---------+
+ | r1 |
+ | 1.1.1.1 | PE Router
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 | P router
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 | r4-eth5
+ | 3.3.3.3 | | 4.4.4.4 |-------+ PE Routers
+ +-----------+ +---------+ |
+192.168.1.1 |r3.eth4 192.168.1.1 | r4-eth4 |192.168.2.1
+ .2 | ceX-eth0 .2 | | .2
+ +-----+-----+ +----+-----+ +----+-----+
+ | ce2 | | ce3 | | ce4 |
+ | 99.0.0.2 | | 99.0.0.3 | | 99.0.0.4 | CE Routers
+ +-----+-----+ +----+-----+ +----+-----+
+ | | |
+
+"""
+
+import os
+import platform
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import get_topogen
+from lib.topolog import logger
+from lib.ltemplate import ltemplateRtrCmd
+
+# Required to instantiate the topology builder class.
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+# test name based on directory
+TEST = os.path.basename(CWD)
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ # Create P/PE routers
+ # check for mpls
+ tgen.add_router("r1")
+ if tgen.hasmpls != True:
+ logger.info("MPLS not available, tests will be skipped")
+ return
+ mach = platform.machine()
+ krel = platform.release()
+ if mach[:1] == "a" and topotest.version_cmp(krel, "4.11") < 0:
+ logger.info("Need Kernel version 4.11 to run on arm processor")
+ return
+ for routern in range(2, 5):
+ tgen.add_router("r{}".format(routern))
+ # Create CE routers
+ for routern in range(1, 5):
+ tgen.add_router("ce{}".format(routern))
+
+ # CE/PE links
+ tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "ce1-eth0", "r1-eth4")
+ tgen.add_link(tgen.gears["ce2"], tgen.gears["r3"], "ce2-eth0", "r3-eth4")
+ tgen.add_link(tgen.gears["ce3"], tgen.gears["r4"], "ce3-eth0", "r4-eth4")
+ tgen.add_link(tgen.gears["ce4"], tgen.gears["r4"], "ce4-eth0", "r4-eth5")
+
+ # Create a switch with just one router connected to it to simulate a
+ # empty network.
+ switch = {}
+ switch[0] = tgen.add_switch("sw0")
+ switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0")
+ switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0")
+
+ switch[1] = tgen.add_switch("sw1")
+ switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1")
+ switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0")
+ switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0")
+
+ switch[1] = tgen.add_switch("sw2")
+ switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth2")
+ switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth1")
+
+
+def ltemplatePreRouterStartHook():
+ cc = ltemplateRtrCmd()
+ krel = platform.release()
+ tgen = get_topogen()
+ logger.info("pre router-start hook, kernel=" + krel)
+
+ # check for mpls
+ if tgen.hasmpls != True:
+ logger.info("MPLS not available, skipping setup")
+ return False
+ # trace errors/unexpected output
+ cc.resetCounts()
+ # configure r2 mpls interfaces
+ intfs = ["lo", "r2-eth0", "r2-eth1", "r2-eth2"]
+ for intf in intfs:
+ cc.doCmd(tgen, "r2", "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf))
+
+ # configure cust1 VRFs & MPLS
+ rtrs = ["r1", "r3", "r4"]
+ cmds = [
+ "ip link add {0}-cust1 type vrf table 10",
+ "ip ru add oif {0}-cust1 table 10",
+ "ip ru add iif {0}-cust1 table 10",
+ "ip link set dev {0}-cust1 up",
+ ]
+ for rtr in rtrs:
+ for cmd in cmds:
+ cc.doCmd(tgen, rtr, cmd.format(rtr))
+ cc.doCmd(tgen, rtr, "ip link set dev {0}-eth4 master {0}-cust1".format(rtr))
+ intfs = [rtr + "-cust1", "lo", rtr + "-eth0", rtr + "-eth4"]
+ for intf in intfs:
+ cc.doCmd(
+ tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf)
+ )
+ logger.info(
+ "setup {0} vrf {0}-cust1, {0}-eth4. enabled mpls input.".format(rtr)
+ )
+ # configure cust2 VRFs & MPLS
+ rtrs = ["r4"]
+ cmds = [
+ "ip link add {0}-cust2 type vrf table 20",
+ "ip ru add oif {0}-cust2 table 20",
+ "ip ru add iif {0}-cust2 table 20",
+ "ip link set dev {0}-cust2 up",
+ ]
+ for rtr in rtrs:
+ for cmd in cmds:
+ cc.doCmd(tgen, rtr, cmd.format(rtr))
+ cc.doCmd(tgen, rtr, "ip link set dev {0}-eth5 master {0}-cust2".format(rtr))
+ intfs = [rtr + "-cust2", rtr + "-eth5"]
+ for intf in intfs:
+ cc.doCmd(
+ tgen, rtr, "echo 1 > /proc/sys/net/mpls/conf/{}/input".format(intf)
+ )
+ logger.info(
+ "setup {0} vrf {0}-cust2, {0}-eth5. enabled mpls input.".format(rtr)
+ )
+ # put ce4-eth0 into a VRF (no default instance!)
+ rtrs = ["ce4"]
+ cmds = [
+ "ip link add {0}-cust2 type vrf table 20",
+ "ip ru add oif {0}-cust2 table 20",
+ "ip ru add iif {0}-cust2 table 20",
+ "ip link set dev {0}-cust2 up",
+ ]
+ for rtr in rtrs:
+ for cmd in cmds:
+ cc.doCmd(tgen, rtr, cmd.format(rtr))
+ cc.doCmd(tgen, rtr, "ip link set dev {0}-eth0 master {0}-cust2".format(rtr))
+ if cc.getOutput() != 0:
+ InitSuccess = False
+ logger.info(
+ "Unexpected output seen ({} times, tests will be skipped".format(
+ cc.getOutput()
+ )
+ )
+ else:
+ rtrs = ["r1", "r3", "r4", "ce4"]
+ for rtr in rtrs:
+ logger.info("{} configured".format(rtr))
+ cc.doCmd(tgen, rtr, "ip -d link show type vrf")
+ cc.doCmd(tgen, rtr, "ip link show")
+ InitSuccess = True
+ logger.info("VRF config successful!")
+ return InitSuccess
+
+
+def ltemplatePostRouterStartHook():
+ logger.info("post router-start hook")
+ return True
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
new file mode 100644
index 0000000..8d42cfc
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/bgpd.conf
@@ -0,0 +1,55 @@
+frr defaults traditional
+
+hostname r1
+password zebra
+log stdout notifications
+log commands
+
+log file bgpd.log debugging
+
+#debug bgp vpn leak-to-vrf
+#debug bgp vpn leak-from-vrf
+#debug bgp vpn label
+#debug bgp updates out
+
+router bgp 5226
+ bgp router-id 1.1.1.1
+ bgp cluster-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 1.1.1.1
+ neighbor 2.2.2.2 timers 3 10
+
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+
+
+router bgp 5227 vrf r1-cust1
+
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+
+ neighbor 192.168.1.2 remote-as 5227
+ neighbor 192.168.1.2 update-source 192.168.1.1
+ neighbor 192.168.1.2 timers 3 10
+
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+
+ label vpn export 101
+ rd vpn export 10:1
+ rt vpn both 52:100
+
+ import vpn
+ export vpn
+ exit-address-family
+
+
+!
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf
new file mode 100644
index 0000000..168b2d4
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r1
+log file ldpd.log
+password zebra
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ !
+ interface r1-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf
new file mode 100644
index 0000000..460a8fb
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf
new file mode 100644
index 0000000..221bc7a
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r1/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+
+hostname r1
+password zebra
+
+#debug zebra packet
+
+interface lo
+ ip address 1.1.1.1/32
+
+interface r1-eth0
+ description to sw0
+ ip address 10.0.1.1/24
+ no link-detect
+
+interface r1-eth4
+ description to ce1
+ ip address 192.168.1.1/24
+ no link-detect
+
+ip forwarding
+
+
+line vty
+
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
new file mode 100644
index 0000000..7b42b77
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/bgpd.conf
@@ -0,0 +1,35 @@
+frr defaults traditional
+
+hostname r2
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log debugging
+
+router bgp 5226
+ bgp router-id 2.2.2.2
+ bgp cluster-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ neighbor 1.1.1.1 remote-as 5226
+ neighbor 1.1.1.1 update-source 2.2.2.2
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 3.3.3.3 remote-as 5226
+ neighbor 3.3.3.3 update-source 2.2.2.2
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 4.4.4.4 remote-as 5226
+ neighbor 4.4.4.4 update-source 2.2.2.2
+ neighbor 4.4.4.4 timers 3 10
+ address-family ipv4 unicast
+ no neighbor 1.1.1.1 activate
+ no neighbor 3.3.3.3 activate
+ no neighbor 4.4.4.4 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 1.1.1.1 activate
+ neighbor 1.1.1.1 route-reflector-client
+ neighbor 3.3.3.3 activate
+ neighbor 3.3.3.3 route-reflector-client
+ neighbor 4.4.4.4 activate
+ neighbor 4.4.4.4 route-reflector-client
+ exit-address-family
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf
new file mode 100644
index 0000000..847be20
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ldpd.conf
@@ -0,0 +1,26 @@
+hostname r2
+log file ldpd.log
+password zebra
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf
new file mode 100644
index 0000000..dbed618
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf
new file mode 100644
index 0000000..dc4ef7e
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+
+hostname r2
+password zebra
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ ip address 10.0.1.2/24
+ no link-detect
+!
+interface r2-eth1
+ description to sw1
+ ip address 10.0.2.2/24
+ no link-detect
+!
+interface r2-eth2
+ description to sw2
+ ip address 10.0.3.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
new file mode 100644
index 0000000..6c9640e
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/bgpd.conf
@@ -0,0 +1,48 @@
+frr defaults traditional
+
+hostname r3
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log
+
+#debug bgp vpn label
+router bgp 5226
+ bgp router-id 3.3.3.3
+ bgp cluster-id 3.3.3.3
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 3.3.3.3
+ neighbor 2.2.2.2 timers 3 10
+
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+
+router bgp 5227 vrf r3-cust1
+
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+
+ neighbor 192.168.1.2 remote-as 5227
+ neighbor 192.168.1.2 update-source 192.168.1.1
+ neighbor 192.168.1.2 timers 3 10
+
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+
+ label vpn export 103
+ rd vpn export 10:3
+ rt vpn both 52:100
+
+ import vpn
+ export vpn
+ exit-address-family
+
+
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf
new file mode 100644
index 0000000..a620739
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r3
+password zebra
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf
new file mode 100644
index 0000000..0e64ed6
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf
new file mode 100644
index 0000000..9ffc84f
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r3/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+
+hostname r3
+password zebra
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ ip address 10.0.2.3/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+ no link-detect
+!
+interface r3-eth4
+ description to ce2
+ ip address 192.168.1.1/24
+ no link-detect
+!
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
new file mode 100644
index 0000000..ca9e627
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/bgpd.conf
@@ -0,0 +1,72 @@
+frr defaults traditional
+
+hostname r4
+password zebra
+log stdout notifications
+log commands
+log file bgpd.log debug
+
+#debug bgp vpn label
+#debug bgp nht
+#debug bgp zebra
+
+router bgp 5226
+ bgp router-id 4.4.4.4
+ bgp cluster-id 4.4.4.4
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 update-source 4.4.4.4
+ neighbor 2.2.2.2 timers 3 10
+
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+
+router bgp 5227 vrf r4-cust1
+
+ bgp router-id 192.168.1.1
+ no bgp ebgp-requires-policy
+
+ neighbor 192.168.1.2 remote-as 5227
+ neighbor 192.168.1.2 update-source 192.168.1.1
+ neighbor 192.168.1.2 timers 3 10
+
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.1.2 next-hop-self
+
+ label vpn export 1041
+ rd vpn export 10:41
+ rt vpn both 52:100
+
+ import vpn
+ export vpn
+ exit-address-family
+
+router bgp 5228 vrf r4-cust2
+
+ bgp router-id 192.168.2.1
+ no bgp ebgp-requires-policy
+
+ neighbor 192.168.2.2 remote-as 5228
+ neighbor 192.168.2.2 update-source 192.168.2.1
+ neighbor 192.168.2.2 timers 3 10
+
+ address-family ipv4 unicast
+ neighbor 192.168.2.2 activate
+ neighbor 192.168.2.2 next-hop-self
+
+ label vpn export 1042
+ rd vpn export 10:42
+ # note RT same as r4-cust1 for inter-vrf route leaking
+ rt vpn both 52:100
+
+ import vpn
+ export vpn
+ exit-address-family
+
+end
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf
new file mode 100644
index 0000000..617d3a7
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r4
+password zebra
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 4.4.4.4
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ !
+ interface r4-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf
new file mode 100644
index 0000000..89e37df
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf
new file mode 100644
index 0000000..4f01a27
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/r4/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+
+hostname r4
+password zebra
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ ip address 10.0.2.4/24
+ no link-detect
+!
+interface r4-eth4
+ description to ce3
+ ip address 192.168.1.1/24
+ no link-detect
+!
+interface r4-eth5
+ description to ce4
+ ip address 192.168.2.1/24
+ no link-detect
+!
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py
new file mode 100644
index 0000000..375bca8
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/add_routes.py
@@ -0,0 +1,59 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1", 'vtysh -c "add vrf r1-cust1 prefix 99.0.0.1/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r3", 'vtysh -c "add vrf r3-cust1 prefix 99.0.0.2/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r4", 'vtysh -c "add vrf r4-cust1 prefix 99.0.0.3/32"', ".", "none", "IP Address"
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.1",
+ "pass",
+ "Local Registration",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.2",
+ "pass",
+ "Local Registration",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.3",
+ "pass",
+ "Local Registration",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations remote"',
+ "4 out of 4",
+ "wait",
+ "Remote Registration",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations remote"',
+ "6 out of 6",
+ "wait",
+ "Remote Registration",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations remote"',
+ "4 out of 4",
+ "wait",
+ "Remote Registration",
+ 10,
+)
+luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py
new file mode 100644
index 0000000..f514575
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/adjacencies.py
@@ -0,0 +1,64 @@
+from lib.lutil import luCommand
+
+luCommand("ce1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand("ce2", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand("ce3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Adjacencies up", 180)
+luCommand(
+ "ce4", 'vtysh -c "show bgp vrf all summary"', " 00:0", "wait", "Adjacencies up", 180
+)
+luCommand(
+ "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp summary"',
+ " 00:0.* 00:0.* 00:0",
+ "wait",
+ "Core adjacencies up",
+ 180,
+)
+luCommand(
+ "r1", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r3", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r4", 'vtysh -c "show bgp summary"', " 00:0", "wait", "Core adjacencies up", 180
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0.* 00:0.* 00:0",
+ "pass",
+ "All adjacencies up",
+)
+luCommand(
+ "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping"
+)
+luCommand(
+ "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping"
+)
+luCommand(
+ "r4", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping"
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py
new file mode 100644
index 0000000..91a7adf
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_mpls.py
@@ -0,0 +1,83 @@
+from lib.lutil import luCommand, luLast
+from lib import topotest
+
+ret = luCommand(
+ "r2",
+ "ip -M route show",
+ r"\d*(?= via inet 10.0.2.4 dev r2-eth1)",
+ "wait",
+ "See mpls route to r4",
+)
+found = luLast()
+
+if ret != False and found != None:
+ label4r4 = found.group(0)
+ luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r4" % label4r4)
+ ret = luCommand(
+ "r2",
+ "ip -M route show",
+ r"\d*(?= via inet 10.0.1.1 dev r2-eth0)",
+ "wait",
+ "See mpls route to r1",
+ )
+ found = luLast()
+
+if ret != False and found != None:
+ label4r1 = found.group(0)
+ luCommand("r2", "ip -M route show", ".", "pass", "See %s as label to r1" % label4r1)
+
+ luCommand(
+ "r1",
+ "ip route show vrf r1-cust1",
+ "99.0.0.4",
+ "pass",
+ "VRF->MPLS PHP route installed",
+ )
+ luCommand(
+ "r4",
+ "ip route show vrf r4-cust2",
+ "99.0.0.1",
+ "pass",
+ "VRF->MPLS PHP route installed",
+ )
+
+ luCommand("r1", "ip -M route show", "101", "pass", "MPLS->VRF route installed")
+ luCommand("r4", "ip -M route show", "1041", "pass", "MPLS->VRF1 route installed")
+ luCommand("r4", "ip -M route show", "1042", "pass", "MPLS->VRF2 route installed")
+
+ luCommand(
+ "ce1",
+ "ping 99.0.0.4 -I 99.0.0.1 -c 1",
+ " 0. packet loss",
+ "wait",
+ "CE->CE (loopback) ping - l3vpn+zebra case",
+ )
+ # skip due to VRF weirdness
+ # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1',
+ # ' 0. packet loss','wait','CE->CE (loopback) ping - l3vpn+zebra case')
+
+ luCommand(
+ "ce1",
+ "ping 99.0.0.4 -I 99.0.0.1 -c 1",
+ " 0. packet loss",
+ "wait",
+ "CE->CE (loopback) ping",
+ )
+ # luCommand('ce4', 'ping 99.0.0.1 -I 99.0.0.4 -c 1',
+ # ' 0. packet loss','wait','CE->CE (loopback) ping')
+
+ luCommand("r3", "ip -M route show", "103", "pass", "MPLS->VRF route installed")
+ luCommand(
+ "ce2",
+ "ping 99.0.0.3 -I 99.0.0.2 -c 1",
+ " 0. packet loss",
+ "wait",
+ "CE2->CE3 (loopback) ping",
+ )
+ luCommand(
+ "ce3",
+ "ping 99.0.0.4 -I 99.0.0.3 -c 1",
+ " 0. packet loss",
+ "wait",
+ "CE3->CE4 (loopback) ping",
+ )
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py
new file mode 100644
index 0000000..75158b1
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_linux_vrf.py
@@ -0,0 +1,74 @@
+from lib.lutil import luCommand
+
+rtrs = ["r1", "r3", "r4"]
+for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip link show type vrf {}-cust1".format(rtr),
+ "cust1: .*UP",
+ "pass",
+ "VRF cust1 intf up",
+ )
+ luCommand(
+ rtr,
+ "ip add show vrf {}-cust1".format(rtr),
+ "r..eth4.*UP",
+ "pass",
+ "VRF cust1 IP intf up",
+ )
+ luCommand(
+ rtr,
+ "ip add show vrf {}-cust1".format(rtr),
+ "192.168",
+ "pass",
+ "VRF cust1 IP config",
+ )
+ luCommand(
+ rtr,
+ "ip route show vrf {}-cust1".format(rtr),
+ "192.168...0/24 dev r.-eth",
+ "pass",
+ "VRF cust1 interface route",
+ )
+luCommand("r4", "ip link show type vrf r4-cust2", "cust2: .*UP", "pass", "VRF cust2 up")
+luCommand(
+ "r4",
+ "ip add show vrf r4-cust2",
+ "r..eth5.*UP.* 192.168",
+ "pass",
+ "VRF cust1 IP config",
+)
+luCommand(
+ "r4",
+ "ip route show vrf r4-cust2",
+ "192.168...0/24 dev r.-eth",
+ "pass",
+ "VRF cust2 interface route",
+)
+rtrs = ["ce1", "ce2", "ce3"]
+for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip route show",
+ "192.168...0/24 dev ce.-eth0",
+ "pass",
+ "CE interface route",
+ )
+ luCommand(rtr, "ping 192.168.1.1 -c 1", " 0. packet loss", "wait", "CE->PE ping")
+luCommand(
+ "ce4", "ip link show type vrf ce4-cust2", "cust2: .*UP", "pass", "VRF cust2 up"
+)
+luCommand(
+ "ce4",
+ "ip route show vrf ce4-cust2",
+ "192.168...0/24 dev ce.-eth0",
+ "pass",
+ "CE interface route",
+)
+luCommand(
+ "ce4",
+ "ping 192.168.2.1 -c 1 -I ce4-cust2",
+ " 0. packet loss",
+ "wait",
+ "CE4->PE4 ping",
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py
new file mode 100644
index 0000000..550ff93
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/check_routes.py
@@ -0,0 +1,888 @@
+from lib.lutil import luCommand
+from lib.bgprib import bgpribRequireVpnRoutes, bgpribRequireUnicastRoutes
+
+########################################################################
+# CE routers: contain routes they originate
+########################################################################
+#
+# mininet CLI commands
+# ce1 vtysh -c "show bgp ipv4 uni"
+# ce2 vtysh -c "show bgp ipv4 uni"
+# ce3 vtysh -c "show bgp ipv4 uni"
+# ce4 vtysh -c "show bgp ipv4 uni"
+
+want = [
+ {"p": "5.1.0.0/24", "n": "99.0.0.1"},
+ {"p": "5.1.1.0/24", "n": "99.0.0.1"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.1"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.1"},
+ {"p": "99.0.0.1/32", "n": "0.0.0.0"},
+]
+bgpribRequireUnicastRoutes("ce1", "ipv4", "", "Cust 1 routes in ce1", want)
+
+want = [
+ {"p": "5.1.0.0/24", "n": "99.0.0.2"},
+ {"p": "5.1.1.0/24", "n": "99.0.0.2"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.2"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.2"},
+ {"p": "99.0.0.2/32", "n": "0.0.0.0"},
+]
+bgpribRequireUnicastRoutes("ce2", "ipv4", "", "Cust 2 routes in ce1", want)
+
+want = [
+ {"p": "5.1.2.0/24", "n": "99.0.0.3"},
+ {"p": "5.1.3.0/24", "n": "99.0.0.3"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.3"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.3"},
+ {"p": "99.0.0.3/32", "n": "0.0.0.0"},
+]
+bgpribRequireUnicastRoutes("ce3", "ipv4", "", "Cust 3 routes in ce1", want)
+
+want = [
+ {"p": "5.4.2.0/24", "n": "99.0.0.4"},
+ {"p": "5.4.3.0/24", "n": "99.0.0.4"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.4"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.4"},
+ {"p": "99.0.0.4/32", "n": "0.0.0.0"},
+]
+bgpribRequireUnicastRoutes("ce4", "ipv4", "ce4-cust2", "Cust 4 routes in ce1", want)
+
+
+########################################################################
+# PE routers: VRFs contain routes from locally-attached customer nets
+########################################################################
+#
+# r1 vtysh -c "show bgp vrf r1-cust1 ipv4"
+#
+want_r1_cust1_routes = [
+ {"p": "5.1.0.0/24", "n": "99.0.0.1"},
+ {"p": "5.1.1.0/24", "n": "99.0.0.1"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.1"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.1"},
+ {"p": "99.0.0.1/32", "n": "192.168.1.2"},
+]
+bgpribRequireUnicastRoutes(
+ "r1", "ipv4", "r1-cust1", "Customer 1 routes in r1 vrf", want_r1_cust1_routes
+)
+
+want_r3_cust1_routes = [
+ {"p": "5.1.0.0/24", "n": "99.0.0.2"},
+ {"p": "5.1.1.0/24", "n": "99.0.0.2"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.2"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.2"},
+ {"p": "99.0.0.2/32", "n": "192.168.1.2"},
+]
+bgpribRequireUnicastRoutes(
+ "r3", "ipv4", "r3-cust1", "Customer 1 routes in r3 vrf", want_r3_cust1_routes
+)
+
+want_r4_cust1_routes = [
+ {"p": "5.1.2.0/24", "n": "99.0.0.3"},
+ {"p": "5.1.3.0/24", "n": "99.0.0.3"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.3"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.3"},
+ {"p": "99.0.0.3/32", "n": "192.168.1.2"},
+]
+bgpribRequireUnicastRoutes(
+ "r4", "ipv4", "r4-cust1", "Customer 1 routes in r4 vrf", want_r4_cust1_routes
+)
+
+want_r4_cust2_routes = [
+ {"p": "5.4.2.0/24", "n": "99.0.0.4"},
+ {"p": "5.4.3.0/24", "n": "99.0.0.4"},
+ {"p": "6.0.1.0/24", "n": "99.0.0.4"},
+ {"p": "6.0.2.0/24", "n": "99.0.0.4"},
+ {"p": "99.0.0.4/32", "n": "192.168.2.2"},
+]
+bgpribRequireUnicastRoutes(
+ "r4", "ipv4", "r4-cust2", "Customer 2 routes in r4 vrf", want_r4_cust2_routes
+)
+
+########################################################################
+# PE routers: core unicast routes are empty
+########################################################################
+
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Core Unicast SAFI clean",
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Core Unicast SAFI clean",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Core Unicast SAFI clean",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Core Unicast SAFI clean",
+)
+
+########################################################################
+# PE routers: local ce-originated routes are leaked to vpn
+########################################################################
+
+# nhzero is for the new code that sets nh of locally-leaked routes to 0
+# nhzero = 1
+nhzero = 0
+
+if nhzero:
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "Distinguisher: *10:1.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.1/32 *0.0.0.0 ",
+ "pass",
+ "vrf->vpn routes",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ "Distinguisher: *10:3.*5.1.0.0/24 *0.0.0.0 .*5.1.1.0/24 *0.0.0.0 .*99.0.0.2/32 *0.0.0.0 ",
+ "pass",
+ "vrf->vpn routes",
+ )
+ want = [
+ {"rd": "10:41", "p": "5.1.2.0/24", "n": "0.0.0.0"},
+ {"rd": "10:41", "p": "5.1.3.0/24", "n": "0.0.0.0"},
+ {"rd": "10:41", "p": "99.0.0.3/32", "n": "0.0.0.0"},
+ {"rd": "10:42", "p": "5.4.2.0/24", "n": "0.0.0.0"},
+ {"rd": "10:42", "p": "5.4.3.0/24", "n": "0.0.0.0"},
+ {"rd": "10:42", "p": "99.0.0.4/32", "n": "0.0.0.0"},
+ ]
+ bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want)
+
+else:
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ r"Distinguisher: *10:1.*5.1.0.0/24 *99.0.0.1\b.*5.1.1.0/24 *99.0.0.1\b.*6.0.1.0/24 *99.0.0.1\b.*6.0.2.0/24 *99.0.0.1\b.*99.0.0.1/32 *192.168.1.2\b",
+ "pass",
+ "vrf->vpn routes",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn"',
+ r"Distinguisher: *10:3.*5.1.0.0/24 *99.0.0.2\b.*5.1.1.0/24 *99.0.0.2\b.*6.0.1.0/24 *99.0.0.2\b.*6.0.2.0/24 *99.0.0.2\b.*99.0.0.2/32 *192.168.1.2\b",
+ "pass",
+ "vrf->vpn routes",
+ )
+ want = [
+ {"rd": "10:41", "p": "5.1.2.0/24", "n": "99.0.0.3"},
+ {"rd": "10:41", "p": "5.1.3.0/24", "n": "99.0.0.3"},
+ {"rd": "10:41", "p": "6.0.1.0/24", "n": "99.0.0.3"},
+ {"rd": "10:41", "p": "6.0.2.0/24", "n": "99.0.0.3"},
+ {"rd": "10:41", "p": "99.0.0.3/32", "n": "192.168.1.2"},
+ {"rd": "10:42", "p": "5.4.2.0/24", "n": "99.0.0.4"},
+ {"rd": "10:42", "p": "5.4.3.0/24", "n": "99.0.0.4"},
+ {"rd": "10:42", "p": "6.0.1.0/24", "n": "99.0.0.4"},
+ {"rd": "10:42", "p": "6.0.2.0/24", "n": "99.0.0.4"},
+ {"rd": "10:42", "p": "99.0.0.4/32", "n": "192.168.2.2"},
+ ]
+ bgpribRequireVpnRoutes("r4", "vrf->vpn routes", want)
+
+########################################################################
+# PE routers: exporting vrfs set MPLS vrf labels in kernel
+########################################################################
+
+luCommand(
+ "r1", 'vtysh -c "show mpls table"', " 101 *BGP *r1-cust1", "pass", "vrf labels"
+)
+luCommand(
+ "r3", 'vtysh -c "show mpls table"', " 103 *BGP *r3-cust1", "pass", "vrf labels"
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show mpls table"',
+ " 1041 *BGP *r4-cust1 .*1042 *BGP *r4-cust2",
+ "pass",
+ "vrf labels",
+)
+
+########################################################################
+# Core VPN router: all customer routes
+########################################################################
+
+want_rd_routes = [
+ {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"},
+ {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"},
+ {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"},
+]
+bgpribRequireVpnRoutes("r2", "Customer routes in provider vpn core", want_rd_routes)
+
+########################################################################
+# PE routers: VPN routes from remote customers
+########################################################################
+#
+# r1 vtysh -c "show bgp ipv4 vpn"
+#
+want_r1_remote_vpn_routes = [
+ {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"},
+ {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"},
+]
+bgpribRequireVpnRoutes(
+ "r1", "Remote Customer routes in R1 vpn", want_r1_remote_vpn_routes
+)
+
+want_r3_remote_vpn_routes = [
+ {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"},
+ {"rd": "10:41", "p": "5.1.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "5.1.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:41", "p": "99.0.0.3/32", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.2.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "5.4.3.0/24", "n": "4.4.4.4"},
+ {"rd": "10:42", "p": "99.0.0.4/32", "n": "4.4.4.4"},
+]
+bgpribRequireVpnRoutes(
+ "r3", "Remote Customer routes in R3 vpn", want_r3_remote_vpn_routes
+)
+
+want_r4_remote_vpn_routes = [
+ {"rd": "10:1", "p": "5.1.0.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "5.1.1.0/24", "n": "1.1.1.1"},
+ {"rd": "10:1", "p": "99.0.0.1/32", "n": "1.1.1.1"},
+ {"rd": "10:3", "p": "5.1.0.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "5.1.1.0/24", "n": "3.3.3.3"},
+ {"rd": "10:3", "p": "99.0.0.2/32", "n": "3.3.3.3"},
+]
+bgpribRequireVpnRoutes(
+ "r4", "Remote Customer routes in R4 vpn", want_r4_remote_vpn_routes
+)
+
+
+# r1 vtysh -c "show bgp vrf r1-cust1 ipv4"
+########################################################################
+# PE routers: VRFs contain routes from remote customer nets
+########################################################################
+# First let's spot check and ensure that some of the routes
+# have showed up and been best path'd
+# After the first two are good. It's probably ok
+# to look at the rest of the routes in the vrf
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.0.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.0.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.1.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.1.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.2.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.1.2.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.1.3.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.1.3.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.4.2.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.4.2.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 5.4.2.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.4.3.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 6.0.1.0/24"',
+ "3 available, best",
+ "wait",
+ "Ensure 6.0.1.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 6.0.2.0/24"',
+ "3 available, best",
+ "wait",
+ "Ensure 6.0.2.0 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.1/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.1 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.2/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.2 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.3/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.3 shows up on r1",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 99.0.0.4/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.4 shows up on r1",
+ 10,
+)
+want_r1_remote_cust1_routes = [
+ {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "5.1.0.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "5.1.1.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "5.1.2.0/24", "n": "4.4.4.4"},
+ {"p": "5.1.3.0/24", "n": "4.4.4.4"},
+ {"p": "5.4.2.0/24", "n": "4.4.4.4"},
+ {"p": "5.4.3.0/24", "n": "4.4.4.4"},
+ {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "4.4.4.4", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": True},
+ {"p": "6.0.2.0/24", "n": "4.4.4.4", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.1", "bp": False},
+ {"p": "99.0.0.1/32", "n": "192.168.1.2", "bp": True},
+ {"p": "99.0.0.2/32", "n": "3.3.3.3"},
+ {"p": "99.0.0.3/32", "n": "4.4.4.4"},
+ {"p": "99.0.0.4/32", "n": "4.4.4.4"},
+]
+bgpribRequireUnicastRoutes(
+ "r1",
+ "ipv4",
+ "r1-cust1",
+ "Customer 1 routes in r1 vrf (2)",
+ want_r1_remote_cust1_routes,
+ debug=False,
+)
+
+
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.0.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.0.0 shows up r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.1.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.1.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.2.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.1.2.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.1.3.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.1.3.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.4.3.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.4.3.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 5.4.3.0/24"',
+ "1 available, best",
+ "wait",
+ "Ensure 5.4.3.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 6.0.1.0/24"',
+ "3 available, best",
+ "wait",
+ "Ensure 6.0.1.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 6.0.2.0/24"',
+ "3 available, best",
+ "wait",
+ "Ensure 6.0.2.0 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.1/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.1 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.3/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.3 shows up on r3",
+ 10,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 99.0.0.4/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.4 shows up on r3",
+ 10,
+)
+want_r3_remote_cust1_routes = [
+ {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.0.0/24", "n": "99.0.0.2", "bp": False},
+ {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "99.0.0.2", "bp": False},
+ {"p": "5.1.2.0/24", "n": "4.4.4.4", "bp": True},
+ {"p": "5.1.3.0/24", "n": "4.4.4.4", "bp": True},
+ {"p": "5.4.2.0/24", "n": "4.4.4.4", "bp": True},
+ {"p": "5.4.3.0/24", "n": "4.4.4.4", "bp": True},
+ {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "4.4.4.4", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.2", "bp": False},
+ {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.2.0/24", "n": "4.4.4.4", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.2", "bp": False},
+ {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True},
+ {"p": "99.0.0.3/32", "n": "4.4.4.4", "bp": True},
+ {"p": "99.0.0.4/32", "n": "4.4.4.4", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "r3",
+ "ipv4",
+ "r3-cust1",
+ "Customer 1 routes in r3 vrf (2)",
+ want_r3_remote_cust1_routes,
+ debug=False,
+)
+
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 5.1.0.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.0.0 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 5.1.1.0/24"',
+ "2 available, best",
+ "wait",
+ "Ensure 5.1.1.0 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 6.0.1.0/24"',
+ "4 available, best",
+ "wait",
+ "Ensure 6.0.1.0 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 6.0.2.0/24"',
+ "4 available, best",
+ "wait",
+ "Ensure 6.0.2.0 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.1/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.1 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.2/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.2 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.3/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.3 shows up on r4",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni 99.0.0.4/32"',
+ "1 available, best",
+ "wait",
+ "Ensure 99.0.0.4 shows up on r4",
+ 10,
+)
+want_r4_remote_cust1_routes = [
+ {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": False},
+ {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": False},
+ {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True},
+ {"p": "99.0.0.2/32", "n": "3.3.3.3", "bp": True},
+ {"p": "99.0.0.3/32", "n": "192.168.1.2", "bp": True},
+ {"p": "99.0.0.4/32", "n": "192.168.2.2", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "r4",
+ "ipv4",
+ "r4-cust1",
+ "Customer 1 routes in r4 vrf (2)",
+ want_r4_remote_cust1_routes,
+ debug=False,
+)
+
+want_r4_remote_cust2_routes = [
+ {"p": "5.1.0.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.0.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "5.1.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": False},
+ {"p": "6.0.2.0/24", "n": "1.1.1.1", "bp": True},
+ {"p": "6.0.2.0/24", "n": "3.3.3.3", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": False},
+ {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": False},
+ {"p": "99.0.0.1/32", "n": "1.1.1.1", "bp": True},
+ {"p": "99.0.0.2/32", "n": "3.3.3.3", "bp": True},
+ {"p": "99.0.0.3/32", "n": "192.168.1.2", "bp": True},
+ {"p": "99.0.0.4/32", "n": "192.168.2.2", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "r4",
+ "ipv4",
+ "r4-cust2",
+ "Customer 2 routes in r4 vrf (2)",
+ want_r4_remote_cust2_routes,
+ debug=False,
+)
+
+
+#########################################################################
+# CE routers: contain routes from remote customer nets
+#########################################################################
+# ce1 vtysh -c "show bgp ipv4 uni"
+# r1 vtysh -c "show bgp vrf r1-cust1 ipv4"
+# r1 vtysh -c "show bgp vrf r1-cust1 ipv4 5.1.2.0/24"
+
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "12 routes and 12",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+want = [
+ {"p": "5.1.0.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "5.1.2.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.1.3.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "99.0.0.1", "bp": True},
+ {"p": "6.0.2.0/24", "n": "99.0.0.1", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "ce1", "ipv4", "", "Cust 1 routes from remote", want, debug=False
+)
+
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "12 routes and 15",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+want = [
+ {"p": "5.1.0.0/24", "n": "192.168.1.1", "bp": False},
+ {"p": "5.1.0.0/24", "n": "99.0.0.2", "bp": True},
+ {"p": "5.1.1.0/24", "n": "192.168.1.1", "bp": False},
+ {"p": "5.1.1.0/24", "n": "99.0.0.2", "bp": True},
+ {"p": "5.1.2.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.1.3.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "192.168.1.1", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.2", "bp": True},
+ {"p": "6.0.2.0/24", "n": "99.0.0.2", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "ce2", "ipv4", "", "Cust 1 routes from remote", want, debug=False
+)
+
+# human readable output for debugging
+luCommand("r4", 'vtysh -c "show bgp vrf r4-cust1 ipv4 uni"')
+luCommand("r4", 'vtysh -c "show bgp vrf r4-cust2 ipv4 uni"')
+luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"')
+luCommand("r4", 'vtysh -c "show ip route vrf r4-cust1"')
+luCommand("r4", 'vtysh -c "show ip route vrf r4-cust2"')
+
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "12 routes and 14",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+# Requires bvl-bug-degenerate-no-label fix (FRR PR #2053)
+want = [
+ {"p": "5.1.0.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.2.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "5.4.3.0/24", "n": "192.168.1.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "192.168.1.1", "bp": False},
+ {"p": "6.0.2.0/24", "n": "192.168.1.1", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.3", "bp": True},
+ {"p": "6.0.2.0/24", "n": "99.0.0.3", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "ce3", "ipv4", "", "Cust 1 routes from remote", want, debug=False
+)
+
+luCommand(
+ "ce4",
+ 'vtysh -c "show bgp vrf ce4-cust2 ipv4 uni"',
+ "12 routes and 14",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+want = [
+ {"p": "5.1.0.0/24", "n": "192.168.2.1", "bp": True},
+ {"p": "5.1.1.0/24", "n": "192.168.2.1", "bp": True},
+ {"p": "5.1.2.0/24", "n": "192.168.2.1", "bp": True},
+ {"p": "5.1.3.0/24", "n": "192.168.2.1", "bp": True},
+ {"p": "6.0.1.0/24", "n": "192.168.2.1", "bp": False},
+ {"p": "6.0.2.0/24", "n": "192.168.2.1", "bp": False},
+ {"p": "6.0.1.0/24", "n": "99.0.0.4", "bp": True},
+ {"p": "6.0.2.0/24", "n": "99.0.0.4", "bp": True},
+]
+bgpribRequireUnicastRoutes(
+ "ce4", "ipv4", "ce4-cust2", "Cust 2 routes from remote", want, debug=False
+)
+
+# verify details of exported/imported routes
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni 6.0.1.0"',
+ "1 available.*192.168.1.1.*99.0.0.1.*Community: 0:67.*Extended Community: RT:89:123.*Large Community: 12:34:56",
+ "pass",
+ "Redundant route 1 details",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni 6.0.1.0"',
+ "2 available, best .*192.168.1.1.* Local.* 192.168.1.1 from 192.168.1.1 .192.168.1.1"
+ + ".* Origin IGP, metric 98, localpref 123, valid, internal"
+ + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:56",
+ "pass",
+ "Redundant route 1 details",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni 6.0.1.0"',
+ "2 available, best .*192.168.1.1.* Local.* 99.0.0.2 from 0.0.0.0 .99.0.0.2"
+ + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:56",
+ "pass",
+ "Redundant route 1 details",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni 6.0.1.0"',
+ "2 available, best "
+ ".* Local.* 99.0.0.3 from 0.0.0.0 .99.0.0.3"
+ + ".* Origin IGP, metric 200, localpref 50, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:56",
+ "pass",
+ "Redundant route 1 details",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni 6.0.1.0"',
+ "2 available, best "
+ ".* Local.* 192.168.1.1 from 192.168.1.1 .192.168.1.1"
+ + ".* Origin IGP, metric 98, localpref 123, valid, internal"
+ + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:56",
+ "pass",
+ "Redundant route 1 details",
+)
+luCommand(
+ "ce4",
+ 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.1.0 json"',
+ (
+ '{"paths":['
+ + '{"aspath":{"string":"Local"},"origin":"IGP","metric":200,"locPrf":50,'
+ + '"weight":32768,"valid":true,"sourced":true,"local":true,'
+ + '"bestpath":{"overall":true,"selectionReason":"Weight"},'
+ + '"community":{"string":"0:67"},"extendedCommunity":{"string":"RT:89:123"},'
+ + '"largeCommunity":{"string":"12:34:56"},'
+ + '"peer":{"peerId":"0.0.0.0","routerId":"99.0.0.4"}},'
+ + '{"aspath":{"string":"Local"},"origin":"IGP","metric":98,"locPrf":123,'
+ + '"valid":true,'
+ + '"community":{"string":"0:67"},"extendedCommunity":{'
+ + '"string":"RT:52:100 RT:89:123"},"largeCommunity":{"string":"12:34:56"},'
+ + '"peer":{"peerId":"192.168.2.1","routerId":"192.168.2.1"}}'
+ + "]}"
+ ),
+ "jsoncmp_pass",
+ "Redundant route 1 details",
+)
+
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "2 available, best .*192.168.1.1.* Local.* 99.0.0.1 from 0.0.0.0 .99.0.0.1"
+ + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:11",
+ "pass",
+ "Redundant route 2 details",
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "2 available, best .*192.168.1.1.* Local.* 99.0.0.2 from 0.0.0.0 .99.0.0.2"
+ + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:12",
+ "pass",
+ "Redundant route 2 details",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "2 available, best .*192.168.1.1.* Local.* 99.0.0.3 from 0.0.0.0 .99.0.0.3"
+ + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:13",
+ "pass",
+ "Redundant route 2 details",
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni 6.0.2.0"',
+ "2 available, best .*192.168.1.1.* Local.* 192.168.1.1 from 192.168.1.1 .192.168.1.1"
+ + ".* Origin IGP, metric 100, localpref 100, valid, internal"
+ + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:11",
+ "pass",
+ "Redundant route 2 details",
+)
+luCommand(
+ "ce4",
+ 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.2.0"',
+ "2 available, best .*192.168.2.1.* Local.* 192.168.2.1 from 192.168.2.1 .192.168.2.1"
+ + ".* Origin IGP, metric 100, localpref 100, valid, internal"
+ + ".* Community: 0:67.* Extended Community: RT:52:100 RT:89:123.* Large Community: 12:34:11",
+ "pass",
+ "Redundant route 2 details",
+)
+luCommand(
+ "ce4",
+ 'vtysh -c "show bgp vrf ce4-cust2 ipv4 6.0.2.0"',
+ "2 available, best .*192.168.2.1.* Local.* 99.0.0.4 from 0.0.0.0 .99.0.0.4"
+ + ".* Origin IGP, metric 100, localpref 100, weight 32768, valid, sourced, local, best .Weight"
+ + ".* Community: 0:67.* Extended Community: RT:89:123.* Large Community: 12:34:14",
+ "pass",
+ "Redundant route 2 details",
+)
+# done
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py
new file mode 100644
index 0000000..a27b178
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/cleanup_all.py
@@ -0,0 +1,120 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1",
+ 'vtysh -c "clear vrf r1-cust1 prefix 99.0.0.1/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.2/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "clear vrf r3-cust1 prefix 99.0.0.3/32"',
+ ".",
+ "none",
+ "Cleared VRF route",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.1",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.2",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "99.0.0.3",
+ "fail",
+ "Local Registration cleared",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+ 10,
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "No BGP prefixes displayed",
+ "pass",
+ "Unicast SAFI",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+ 10,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Unicast SAFI updated",
+ 10,
+)
+luCommand(
+ "ce1",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+luCommand(
+ "ce2",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+luCommand(
+ "ce3",
+ 'vtysh -c "show bgp ipv4 uni"',
+ "2 routes and 2",
+ "wait",
+ "Local and remote routes",
+ 10,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations remote"',
+ "Prefix ",
+ "fail",
+ "Remote Registration cleared",
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py
new file mode 100644
index 0000000..fcbc3df
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/del_bgp_instances.py
@@ -0,0 +1,30 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1",
+ '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r1-cust1" -c "no router bgp 5226"',
+ ".",
+ "none",
+ "Cleared bgp instances",
+)
+luCommand(
+ "r2",
+ '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5226"',
+ ".",
+ "none",
+ "Cleared bgp instances",
+)
+luCommand(
+ "r3",
+ '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5227 vrf r3-cust1" -c "no router bgp 5226"',
+ ".",
+ "none",
+ "Cleared bgp instances",
+)
+luCommand(
+ "r4",
+ '/usr/lib/frr/vtysh -c "conf ter" -c "no router bgp 5228 vrf r4-cust2" -c "no router bgp 5227 vrf r4-cust1" -c "no router bgp 5226"',
+ ".",
+ "none",
+ "Cleared bgp instances",
+)
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py
new file mode 100644
index 0000000..73cd08f
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/notification_check.py
@@ -0,0 +1,22 @@
+from lib.lutil import luCommand, luLast
+
+rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"]
+for rtr in rtrs:
+ ret = luCommand(
+ rtr,
+ 'vtysh -c "show bgp neigh"',
+ "Notification received .([A-Za-z0-9/ ]*)",
+ "none",
+ "collect neighbor stats",
+ )
+ found = luLast()
+ if ret != False and found != None:
+ val = found.group(1)
+ ret = luCommand(
+ rtr,
+ 'vtysh -c "show bgp neigh"',
+ "Notification received",
+ "fail",
+ "Notify RXed! {}".format(val),
+ )
+# done
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py
new file mode 100644
index 0000000..36be926
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_down.py
@@ -0,0 +1,87 @@
+from lib.lutil import luCommand, luLast
+
+ret = luCommand(
+ "ce1",
+ 'vtysh -c "show ip route" | grep -c \\ 10\\.\\*/32',
+ "(.*)",
+ "pass",
+ "Looking for sharp routes",
+)
+found = luLast()
+if ret != False and found != None:
+ num = int(found.group())
+ luCommand(
+ "ce3", 'vtysh -c "show bgp sum"', ".", "pass", "See %s sharp routes" % num
+ )
+ if num > 0:
+ rtrs = ["ce1", "ce2", "ce3"]
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ 'vtysh -c "show bgp ipv4 uni" | grep Display',
+ ".",
+ "none",
+ "BGP routes pre remove",
+ )
+ luCommand(
+ rtr,
+ "ip route show | cat -n | tail",
+ ".",
+ "none",
+ "Linux routes pre remove",
+ )
+ wait = 2 * num / 500
+ luCommand(
+ "ce1",
+ 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),
+ ".",
+ "none",
+ "Removing {} routes".format(num),
+ )
+ luCommand(
+ "ce2",
+ 'vtysh -c "sharp remove routes 10.0.0.0 {}"'.format(num),
+ ".",
+ "none",
+ "Removing {} routes".format(num),
+ )
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ 'vtysh -c "show bgp ipv4 uni" | grep Display',
+ " 12 route",
+ "wait",
+ "BGP routes removed",
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ rtr,
+ 'vtysh -c "show bgp ipv4 uni"',
+ ".",
+ "none",
+ "BGP routes post remove",
+ )
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip route show | grep -c \\^10\\.",
+ "^0$",
+ "wait",
+ "Linux routes removed",
+ wait,
+ wait_time=10,
+ )
+ luCommand(rtr, "ip route show", ".", "none", "Linux routes post remove")
+ rtrs = ["r1", "r3", "r4"]
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr),
+ "^0$",
+ "wait",
+ "VRF route removed",
+ wait,
+ wait_time=10,
+ )
+# done
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
new file mode 100644
index 0000000..eaa6aa4
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/scripts/scale_up.py
@@ -0,0 +1,234 @@
+from lib.lutil import luCommand, luLast
+
+num = 50000
+b = int(num / (256 * 256))
+if b > 0:
+ r = num - b * (256 * 256)
+else:
+ r = num
+c = int(r / 256)
+if c > 0:
+ d = r - c * 256 - 1
+else:
+ d = r
+wait = 2 * num / 1000
+mem_z = {}
+mem_b = {}
+rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"]
+for rtr in rtrs:
+ mem_z[rtr] = {"value": 0, "units": "unknown"}
+ mem_b[rtr] = {"value": 0, "units": "unknown"}
+ ret = luCommand(
+ rtr,
+ 'vtysh -c "show memory"',
+ r"zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)",
+ "none",
+ "collect bgpd memory stats",
+ )
+ found = luLast()
+ if ret != False and found != None:
+ mem_z[rtr] = {"value": int(found.group(1)), "units": found.group(2)}
+ mem_b[rtr] = {"value": int(found.group(3)), "units": found.group(4)}
+
+luCommand(
+ "ce1", 'vtysh -c "show mem"', "qmem sharpd", "none", "check if sharpd running"
+)
+doSharp = False
+found = luLast()
+if ret != False and found != None:
+ if len(found.group()):
+ doSharp = True
+
+if doSharp != True:
+ luCommand(
+ "ce1",
+ 'vtysh -c "sharp data nexthop"',
+ ".",
+ "pass",
+ "sharpd NOT running, skipping test",
+ )
+else:
+ luCommand(
+ "ce1",
+ 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.1 {}"'.format(num),
+ "",
+ "pass",
+ "Adding {} routes".format(num),
+ )
+ luCommand(
+ "ce2",
+ 'vtysh -c "sharp install routes 10.0.0.0 nexthop 99.0.0.2 {}"'.format(num),
+ "",
+ "pass",
+ "Adding {} routes".format(num),
+ )
+ rtrs = ["ce1", "ce2", "ce3"]
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ 'vtysh -c "show bgp ipv4 uni 10.{}.{}.{}"'.format(b, c, d),
+ "Last update:",
+ "wait",
+ "RXed last route, 10.{}.{}.{}".format(b, c, d),
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ rtr,
+ 'vtysh -c "show bgp ipv4 uni" | grep -c 10\\.\\*/32',
+ str(num),
+ "wait",
+ "See all sharp routes in BGP",
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf r1-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d),
+ "99.0.0.1",
+ "wait",
+ "RXed -> 10.{}.{}.{} from CE1".format(b, c, d),
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf r3-cust1 ipv4 uni 10.{}.{}.{}"'.format(b, c, d),
+ "99.0.0.2",
+ "wait",
+ "RXed -> 10.{}.{}.{} from CE2".format(b, c, d),
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "99.0.0.1",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d),
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "99.0.0.2",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d),
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "1.1.1.1",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d),
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "3.3.3.3",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d),
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "1.1.1.1",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE1".format(b, c, d),
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show bgp ipv4 vpn 10.{}.{}.{}"'.format(b, c, d),
+ "3.3.3.3",
+ "wait",
+ "see VPN safi -> 10.{}.{}.{} from CE2".format(b, c, d),
+ )
+ rtrs = ["ce1", "ce2", "ce3"]
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip route get 10.{}.{}.{}".format(b, c, d),
+ "dev",
+ "wait",
+ "Route to 10.{}.{}.{} available".format(b, c, d),
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ rtr,
+ "ip route show | grep -c \\^10\\.",
+ str(num),
+ "wait",
+ "See {} linux routes".format(num),
+ wait,
+ wait_time=10,
+ )
+
+ rtrs = ["r1", "r3", "r4"]
+ for rtr in rtrs:
+ luCommand(
+ rtr,
+ "ip route get vrf {}-cust1 10.{}.{}.{}".format(rtr, b, c, d),
+ "dev",
+ "wait",
+ "VRF route available",
+ wait,
+ wait_time=10,
+ )
+ luCommand(
+ rtr,
+ "ip route show vrf {}-cust1 | grep -c \\^10\\.".format(rtr),
+ str(num),
+ "wait",
+ "See {} linux routes".format(num),
+ wait,
+ wait_time=10,
+ )
+ rtrs = ["ce1", "ce2", "ce3", "r1", "r2", "r3", "r4"]
+ for rtr in rtrs:
+ ret = luCommand(
+ rtr,
+ 'vtysh -c "show memory"',
+ r"zebra: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*) .*bgpd: System allocator statistics: Total heap allocated: *(\d*) ([A-Za-z]*)",
+ "none",
+ "collect bgpd memory stats",
+ )
+ found = luLast()
+ if ret != False and found != None:
+ val_z = int(found.group(1))
+ if mem_z[rtr]["units"] != found.group(2):
+ val_z *= 1000
+ delta_z = val_z - int(mem_z[rtr]["value"])
+ ave_z = float(delta_z) / float(num)
+
+ val_b = int(found.group(3))
+ if mem_b[rtr]["units"] != found.group(4):
+ val_b *= 1000
+ delta_b = val_b - int(mem_b[rtr]["value"])
+ ave_b = float(delta_b) / float(num)
+ luCommand(
+ rtr,
+ 'vtysh -c "show thread cpu"',
+ ".",
+ "pass",
+ "BGPd heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format(
+ mem_b[rtr]["value"],
+ mem_b[rtr]["units"],
+ found.group(3),
+ found.group(4),
+ round(ave_b, 4),
+ ),
+ )
+ luCommand(
+ rtr,
+ 'vtysh -c "show thread cpu"',
+ ".",
+ "pass",
+ "Zebra heap: {0} {1} --> {2} {3} ({4} {1}/vpn route)".format(
+ mem_z[rtr]["value"],
+ mem_z[rtr]["units"],
+ found.group(1),
+ found.group(2),
+ round(ave_z, 4),
+ ),
+ )
+# done
diff --git a/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
new file mode 100755
index 0000000..3844b5e
--- /dev/null
+++ b/tests/topotests/bgp_l3vpn_to_bgp_vrf/test_bgp_l3vpn_to_bgp_vrf.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import sys
+import pytest
+
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "../"))
+
+from lib.ltemplate import *
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def test_check_linux_vrf():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/check_linux_vrf.py", False, CliOnFail, CheckFunc)
+
+
+def test_adjacencies():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc)
+
+
+def test_notification_check():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/notification_check.py", False, CliOnFail, CheckFunc)
+
+
+def SKIP_test_add_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc)
+
+
+# manual data path setup test - remove once have bgp/zebra vrf path working
+def test_check_linux_mpls():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/check_linux_mpls.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_scale_up():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/scale_up.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_scale_down():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1', iproute2='4.9')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True, iproute2=\'4.9\')'
+ ltemplateTest("scripts/scale_down.py", False, CliOnFail, CheckFunc)
+
+
+def SKIP_test_cleanup_all():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('4.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'4.1\', cli=True)'
+ ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc)
+
+
+if __name__ == "__main__":
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/bgp_large_community/__init__.py b/tests/topotests/bgp_large_community/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_large_community/__init__.py
diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json
new file mode 100644
index 0000000..902c01b
--- /dev/null
+++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_1.json
@@ -0,0 +1,262 @@
+{
+ "ipv4base": "192.168.1.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.1.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "1000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "4000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r4-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "6000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r5-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r5-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json
new file mode 100644
index 0000000..36dee39
--- /dev/null
+++ b/tests/topotests/bgp_large_community/bgp_large_community_topo_2.json
@@ -0,0 +1,344 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "1000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "1000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "4000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "5000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r6": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "6000000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r6": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r6": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
new file mode 100644
index 0000000..03cfded
--- /dev/null
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_1.py
@@ -0,0 +1,1229 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 large-community/community functionality:
+1. Verify if large community attribute can be configured only in correct
+ canonical format.
+2. Verify that the community attribute value, which we have advertised are
+ received in correct format and values, at the receiving end.
+3. Verify BGP Large Community attribute"s transitive property attribute.
+4. Verify that BGP Large Communities attribute are malformed, if the length of
+ the BGP Large Communities Attribute value, expressed in octets,
+ is not a non-zero multiple of 12.
+5. Verify if overriding large community values works fine.
+6. Verify that large community values" aggregation works fine.
+7. Standard community also work fine in conjunction with large-community.
+8. Matching prefixes based on attributes other than prefix list and make use
+ of set clause (IPV6).
+9. Matching prefixes based on attributes other than prefix list and make use
+ of set clause (IPV4).
+10. Verify community and large-community list operations in route-map with all
+ clause (exact, all, any, regex) works.
+11. Verify that any value in BGP Large communities for boundary values.
+12. Clear BGP neighbor-ship and check if large community and community
+ attributes are getting re-populated.
+
+"""
+
+import pytest
+import time
+from os import path as os_path
+import sys
+
+# Required to instantiate the topology builder class.
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_route_maps,
+ create_bgp_community_lists,
+ create_prefix_lists,
+ verify_bgp_community,
+ step,
+ check_address_types,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# 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/"))
+
+
+# Global variables
+bgp_convergence = False
+NETWORK = {
+ "ipv4": ["200.50.2.0", "200.50.2.1", "200.50.2.0"],
+ "ipv6": ["1::1", "1::2", "1::0"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NET_MASK = {"ipv4": "24", "ipv6": "120"}
+IPV4_NET = ["200.50.2.0"]
+IPV6_NET = ["1::0"]
+CONFIG_ROUTER_R1 = False
+CONFIG_ROUTER_R2 = False
+CONFIG_ROUTER_ADDITIVE = False
+ADDR_TYPES = []
+LARGE_COMM = {
+ "r1": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1",
+ "r2": "2:1:1 2:2:1 2:3:1 2:4:1 2:5:1",
+ "mal_1": "1:1 1:2 1:3 1:4 1:5",
+ "pf_list_1": "0:0:1 0:0:10 0:0:100",
+ "pf_list_2": "0:0:2 0:0:20 0:0:200",
+ "agg_1": "0:0:1 0:0:2 0:0:10 0:0:20 0:0:100 0:0:200 2:1:1 "
+ "2:2:1 2:3:1 2:4:1 2:5:1",
+ "agg_2": "0:0:2 0:0:20 0:0:200 2:1:1 " "2:2:1 2:3:1 2:4:1 2:5:1",
+}
+STANDARD_COMM = {
+ "r1": "1:1 1:2 1:3 1:4 1:5",
+ "r2": "2:1 2:2 2:3 2:4 2:5",
+ "mal_1": "1 2 3 4 5",
+ "pf_list_1": "0:1 0:10 0:100",
+ "pf_list_2": "0:2 0:20 0:200",
+ "agg_1": "0:1 0:2 0:10 0:20 0:100 0:200 2:1 2:2 2:3 2:4 2:5",
+ "agg_2": "0:2 0:20 0:200 2:1 2:2 2:3 2:4 2:5",
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ global ADDR_TYPES
+ 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...
+ json_file = "{}/bgp_large_community_topo_1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global bgp_convergence
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ##tgen.mininet_cli()
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ ADDR_TYPES = check_address_types()
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def config_router_r1(tgen, topo, tc_name):
+ global CONFIG_ROUTER_R1
+
+ input_dict_1 = {
+ "r1": {
+ "route_maps": {
+ "LC1": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": LARGE_COMM["r1"]},
+ "community": {"num": STANDARD_COMM["r1"]},
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ step("Configuring LC1 on r1")
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv4"][0], MASK["ipv4"]),
+ "no_of_network": 4,
+ }
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {"name": "LC1", "direction": "out"}
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {"name": "LC1", "direction": "out"}
+ ]
+ }
+ }
+ },
+ },
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv6"][0], MASK["ipv6"]),
+ "no_of_network": 4,
+ }
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {"name": "LC1", "direction": "out"}
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {"name": "LC1", "direction": "out"}
+ ]
+ }
+ }
+ },
+ },
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Applying LC1 on r1 neighbors and advertising networks")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ CONFIG_ROUTER_R1 = True
+
+
+def config_router_r2(tgen, topo, tc_name):
+ global CONFIG_ROUTER_R2
+
+ input_dict = {
+ "r2": {
+ "route_maps": {
+ "LC2": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": LARGE_COMM["r2"]},
+ "community": {"num": STANDARD_COMM["r2"]},
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ step("Configuring route-maps LC2 on r2")
+ result = create_route_maps(tgen, input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_1 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {"name": "LC2", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {"name": "LC2", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Applying LC2 on r2 neighbors in out direction")
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ CONFIG_ROUTER_R2 = True
+
+
+def config_router_additive(tgen, topo, tc_name):
+ global CONFIG_ROUTER_ADDITIVE
+
+ input_dict = {
+ "r2": {
+ "route_maps": {
+ "LC2": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": LARGE_COMM["r2"],
+ "action": "additive",
+ },
+ "community": {
+ "num": STANDARD_COMM["r2"],
+ "action": "additive",
+ },
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ step("Configuring LC2 with community attributes as additive")
+ result = create_route_maps(tgen, input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ # tgen.mininet_cli()
+ CONFIG_ROUTER_ADDITIVE = True
+
+
+def config_for_as_path(tgen, topo, tc_name):
+ config_router_r1(tgen, topo, tc_name)
+
+ config_router_r2(tgen, topo, tc_name)
+
+ # Create ipv6 prefix list
+ input_dict_1 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "%s/%s" % (NETWORK["ipv4"][0], MASK["ipv4"]),
+ "action": "permit",
+ }
+ ],
+ "pf_list_2": [
+ {
+ "seqid": "10",
+ "network": "%s/%s" % (NETWORK["ipv4"][1], MASK["ipv4"]),
+ "action": "permit",
+ }
+ ],
+ },
+ "ipv6": {
+ "pf_list_3": [
+ {
+ "seqid": "10",
+ "network": "%s/%s" % (NETWORK["ipv6"][0], MASK["ipv6"]),
+ "action": "permit",
+ }
+ ],
+ "pf_list_4": [
+ {
+ "seqid": "10",
+ "network": "%s/%s" % (NETWORK["ipv6"][1], MASK["ipv6"]),
+ "action": "permit",
+ }
+ ],
+ },
+ }
+ }
+ }
+
+ step("Configuring prefix-lists on r1 to filter networks")
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "LC1": [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {"ipv4": {"prefix_lists": "pf_list_1"}},
+ "set": {
+ "large_community": {"num": LARGE_COMM["pf_list_1"]},
+ "community": {"num": STANDARD_COMM["pf_list_1"]},
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {"ipv6": {"prefix_lists": "pf_list_3"}},
+ "set": {
+ "large_community": {"num": LARGE_COMM["pf_list_1"]},
+ "community": {"num": STANDARD_COMM["pf_list_1"]},
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": 30,
+ "match": {"ipv4": {"prefix_lists": "pf_list_2"}},
+ "set": {
+ "large_community": {"num": LARGE_COMM["pf_list_2"]},
+ "community": {"num": STANDARD_COMM["pf_list_2"]},
+ },
+ },
+ {
+ "action": "permit",
+ "seq_id": 40,
+ "match": {"ipv6": {"prefix_lists": "pf_list_4"}},
+ "set": {
+ "large_community": {"num": LARGE_COMM["pf_list_2"]},
+ "community": {"num": STANDARD_COMM["pf_list_2"]},
+ },
+ },
+ ]
+ }
+ }
+ }
+
+ step(
+ "Applying prefix-lists match in route-map LC1 on r1. Setting"
+ " community attritbute for filtered networks"
+ )
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ config_router_additive(tgen, topo, tc_name)
+
+ input_dict_3 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": LARGE_COMM["pf_list_1"],
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": STANDARD_COMM["pf_list_1"],
+ },
+ ]
+ }
+ }
+
+ step("Configuring bgp community lists on r4")
+ result = create_bgp_community_lists(tgen, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_4 = {
+ "r4": {
+ "route_maps": {
+ "LC4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {
+ "large_community_list": {"id": "ANY"},
+ "community_list": {"id": "ANY"},
+ },
+ "set": {"path": {"as_num": "4000000", "as_action": "prepend"}},
+ }
+ ]
+ }
+ }
+ }
+
+ step("Applying community list on route-map on r4")
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_5 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {"name": "LC4", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {"name": "LC4", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Applying route-map LC4 out from r4 to r5 ")
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+
+#####################################################
+#
+# Test cases
+#
+#####################################################
+def test_large_community_set(request):
+ """
+ Verify if large community attribute can be configured only in correct
+ canonical format.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # API call to modify router id
+ # input_dict dictionary to be provided to configure route_map
+ input_dict = {
+ "r1": {
+ "route_maps": {
+ "LC1": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": LARGE_COMM["r1"]},
+ "community": {"num": STANDARD_COMM["r1"]},
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ step("Trying to set bgp communities")
+ result = create_route_maps(tgen, input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_advertise(request):
+ """
+ Verify that the community attribute value, which we have advertised are
+ received in correct format and values, at the receiving end.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_router_r1(tgen, topo, tc_name)
+
+ input_dict = {
+ "largeCommunity": LARGE_COMM["r1"],
+ "community": STANDARD_COMM["r1"],
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_community(tgen, adt, "r3", [NETWORK[adt][0]], input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_transitive(request):
+ """
+ Verify BGP Large Community attribute"s transitive property attribute.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ config_router_r1(tgen, topo, tc_name)
+
+ input_dict_1 = {
+ "largeCommunity": LARGE_COMM["r1"],
+ "community": STANDARD_COMM["r1"],
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_override(request):
+ """
+ Verify if overriding large community values works fine.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_router_r1(tgen, topo, tc_name)
+
+ config_router_r2(tgen, topo, tc_name)
+
+ input_dict_3 = {
+ "largeCommunity": LARGE_COMM["r2"],
+ "community": STANDARD_COMM["r2"],
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][1]], input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_additive(request):
+ """
+ Verify that large community values" aggregation works fine.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_router_r1(tgen, topo, tc_name)
+
+ config_router_r2(tgen, topo, tc_name)
+
+ config_router_additive(tgen, topo, tc_name)
+
+ input_dict_1 = {
+ "largeCommunity": "%s %s" % (LARGE_COMM["r1"], LARGE_COMM["r2"]),
+ "community": "%s %s" % (STANDARD_COMM["r1"], STANDARD_COMM["r2"]),
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_match_as_path(request):
+ """
+ Matching prefixes based on attributes other than prefix list and make use
+ of set clause.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_for_as_path(tgen, topo, tc_name)
+
+ input_dict = {
+ "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_1"], LARGE_COMM["r2"]),
+ "community": "%s %s" % (STANDARD_COMM["pf_list_1"], STANDARD_COMM["r2"]),
+ }
+
+ input_dict_1 = {
+ "largeCommunity": "%s %s" % (LARGE_COMM["pf_list_2"], LARGE_COMM["r2"]),
+ "community": "%s %s" % (STANDARD_COMM["pf_list_2"], STANDARD_COMM["r2"]),
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r5", [NETWORK[adt][0]], input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_community(
+ tgen, adt, "r5", [NETWORK[adt][1]], input_dict_1, expected=False
+ )
+
+ assert result is not True, "Test case {} : Should fail \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_match_all(request):
+ """
+ Verify community and large-community list operations in route-map with all
+ clause (exact, all, any, regex) works.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_router_r1(tgen, topo, tc_name)
+
+ config_router_r2(tgen, topo, tc_name)
+
+ config_router_additive(tgen, topo, tc_name)
+
+ input_dict_1 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "1:1:1",
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ALL",
+ "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1",
+ "large": True,
+ },
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "EXP_ALL",
+ "value": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:[1-5]:1",
+ "large": True,
+ },
+ ]
+ }
+ }
+
+ step("Create bgp community lists for ANY, EXACT and EXP_ALL match")
+
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "r4": {
+ "route_maps": {
+ "LC4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"large-community-list": {"id": "ANY"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"large-community-list": {"id": "EXACT"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "30",
+ "match": {"large-community-list": {"id": "EXP_ALL"}},
+ },
+ ]
+ }
+ }
+ }
+
+ step("Applying bgp community lits on LC4 route-map")
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {"name": "LC4", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {"name": "LC4", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Apply route-mpa LC4 on r4 for r2 neighbor, direction 'in'")
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_4 = {
+ "largeCommunity": "1:1:1 1:2:1 1:3:1 1:4:1 1:5:1 2:1:1 2:2:1 2:3:1 "
+ "2:4:1 2:5:1"
+ }
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r4", [NETWORK[adt][0]], input_dict_4)
+ assert result is True, "Test case {} : Should fail \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+# @pytest.mark.skip(reason="as-set not working for ipv6")
+def test_large_community_aggregate_network(request):
+ """
+ Restart router and check if large community and community
+ attributes are getting re-populated.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ config_for_as_path(tgen, topo, tc_name)
+
+ input_dict = {
+ "community": STANDARD_COMM["agg_1"],
+ "largeCommunity": LARGE_COMM["agg_1"],
+ }
+
+ input_dict_1 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv4"][2], NET_MASK["ipv4"]),
+ "as_set": True,
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv6"][2], NET_MASK["ipv6"]),
+ "as_set": True,
+ }
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Configuring aggregate address as-set on r2")
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv4"][0], MASK["ipv4"]),
+ "no_of_network": 1,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "%s/%s"
+ % (NETWORK["ipv6"][0], MASK["ipv6"]),
+ "no_of_network": 1,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+
+ step("Stop advertising one of the networks")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "community": STANDARD_COMM["agg_2"],
+ "largeCommunity": LARGE_COMM["agg_2"],
+ }
+
+ for adt in ADDR_TYPES:
+ step("Verifying bgp community values on r5 is also modified")
+ result = verify_bgp_community(
+ tgen, adt, "r4", ["%s/%s" % (NETWORK[adt][2], NET_MASK[adt])], input_dict_3
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_boundary_values(request):
+ """
+ Verify that any value in BGP Large communities for boundary values.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ input_dict = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "0:-1",
+ }
+ ]
+ }
+ }
+
+ step("Checking boundary value for community 0:-1")
+ result = create_bgp_community_lists(tgen, input_dict)
+ assert result is not True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Checking community attribute 0:65536")
+ input_dict_2 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "0:65536",
+ }
+ ]
+ }
+ }
+
+ step("Checking boundary value for community 0:65536")
+ result = create_bgp_community_lists(tgen, input_dict_2)
+ assert result is not True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Checking boundary value for community 0:4294967296")
+ input_dict_3 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "0:4294967296",
+ "large": True,
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_3)
+ assert result is not True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Checking boundary value for community 0:-1:1")
+
+ input_dict_4 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "0:-1:1",
+ "large": True,
+ }
+ ]
+ }
+ }
+
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is not True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+def test_large_community_invalid_chars(request):
+ """
+ BGP canonical lcommunities must only be digits
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ input_dict = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "1:a:2",
+ "large": True,
+ }
+ ]
+ }
+ }
+
+ step("Checking boundary value for community 1:a:2")
+ result = create_bgp_community_lists(tgen, input_dict)
+ assert result is not True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+def test_large_community_after_clear_bgp(request):
+ """
+ Clear BGP neighbor-ship and check if large community and community
+ attributes are getting re-populated.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ config_router_r1(tgen, topo, tc_name)
+
+ input_dict = {"largeCommunity": LARGE_COMM["r1"], "community": STANDARD_COMM["r1"]}
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clearing BGP on r1")
+ clear_bgp_and_verify(tgen, topo, "r1")
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, "r2", [NETWORK[adt][0]], input_dict)
+ assert result is True, "Test case {} : 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/bgp_large_community/test_bgp_large_community_topo_2.py b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
new file mode 100644
index 0000000..b11cda3
--- /dev/null
+++ b/tests/topotests/bgp_large_community/test_bgp_large_community_topo_2.py
@@ -0,0 +1,2233 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+test_bgp_large_community_topo_1.py: Test BGP large community.
+
+Following tests are covered:
+1. Verify the standard large-community-lists can permit or deny
+ large community attribute only in the correct canonical format.
+2. Verify the expanded large-community-lists can permit or deny
+ large community attribute both in the correct canonical format
+ as well as REG_EX.
+3. Verify that we can modify a large-community-list is in use,
+ to add/remove attribute value and it takes immediate effect.
+4. Verify that large community attribute gets advertised when
+ route-map is applied to a neighbor and cleared when route-map
+ is removed.
+5. Verify that duplicate BGP Large Community values are NOT be transmitted.
+6. Verify if we want to remove all the large-community attributes from a
+ set of prefix we can set the value as NONE.
+7. Redistribute connected and static routes in BGP process with a route-map
+ appending/removing L-comm attributes.
+8. Verify if we want to remove specific large-community values from
+ a set of prefix we can make use of DELETE operation based on L-comm list.
+9. Verify that if community values are NOT be advertised to a specific
+ neighbour, we negate send-community command.
+ (Send-community all is enabled by default for all neighbors)
+10. Verify that large-community lists can not be configured without providing
+ specific L-community values(for match/delete operation in a route-map).
+11. Verify that Match_EXACT clause should pass only if all of the L-comm
+ values configured (horizontally) in the community list is present in
+ the prefix. There must be no additional L-communities in the prefix.
+12. Verify that Match_ALL clause should pass only if ALL of the L-comm values
+ configured (horizontally) in the community list is present in the prefix.
+ There could be additional L-communities in the prefix that are not present
+ in the L-comm list.
+13. Verify that Match_ANY clause should pass only if at-least any one L-comm
+ value configured(vertically) in large-community list, is present in prefixes.
+14. Verify large-community lists operation in a route-map with match RegEx
+ statements.
+"""
+
+import os
+import sys
+import pytest
+import time
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+# Import topoJson from lib, to create topology and initial configuration
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_route_maps,
+ create_bgp_community_lists,
+ verify_bgp_community,
+ step,
+ verify_create_community_list,
+ delete_route_maps,
+ verify_route_maps,
+ create_static_routes,
+ check_address_types,
+ required_linux_kernel_version,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+bgp_convergence = False
+
+NETWORKS = {"ipv4": ["200.50.2.0/32"], "ipv6": ["1::1/128"]}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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...
+ json_file = "{}/bgp_large_community_topo_2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global bgp_convergence, ADDR_TYPES
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ # Ipv4
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+ ADDR_TYPES = check_address_types()
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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 test_create_bgp_standard_large_community_list(request):
+ """
+ Create standard large-community-list and verify it can permit
+ or deny large community attribute only in the correct canonical
+ format.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ step("Create srtandard large community list")
+ input_dict = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "LC_1_STD",
+ "value": "2:1:1 2:1:2 1:2:3",
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "LC_2_STD",
+ "value": "3:1:1 3:1:2",
+ "large": True,
+ },
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create srtandard large community list with in-correct values")
+ input_dict = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "LC_1_STD_ERR",
+ "value": "0:0:0",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ## TODO should fail
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_create_bgp_expanded_large_community_list(request):
+ """
+ Create expanded large-community-list and verify it can permit
+ or deny large community attribute both in the correct canonical
+ format as well as REG_EX
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create expanded large community list")
+ input_dict = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "LC_1_EXP",
+ "value": "1:1:200 1:2:* 3:2:1",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_large_community_lists_referenced_by_rmap(request):
+ """
+ This test is to verify that we can modify a large-community-list
+ is in use, add/remove attribute value and it takes immediate effect.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create standard large community list")
+ input_dict_1 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "LC_DEL",
+ "value": "1:2:1 1:3:1 2:1:1 2:2:2 3:3:3",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r1": {
+ "route_maps": {
+ "RM_R2_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "1:2:1 1:3:1 2:10:1 3:3:3 4:4:4 5:5:5",
+ "action": "additive",
+ }
+ },
+ }
+ ]
+ }
+ },
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"large_comm_list": {"id": "LC_DEL", "delete": True}},
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RM_R2_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [{"network": "1::1/128"}],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RM_R2_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ }
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify Community-list")
+ dut = "r4"
+ input_dict_4 = {"largeCommunity": "2:10:1 4:4:4 5:5:5"}
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_apply_and_remove(request):
+ """
+ This test is to verify that large community attribute gets advertised when
+ route-map is applied to a neighbor and cleared when route-map is removed
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_1 = {
+ "r4": {
+ "route_maps": {
+ "RM_LC1": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "200:200:1 200:200:10 200:200:20000",
+ "action": "additive",
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_LC1", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_LC1", "direction": "out"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r6"
+ input_dict_4 = {"largeCommunity": "200:200:1 200:200:10 200:200:20000"}
+
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete route map reference by community-list")
+ input_dict_3 = {"r4": {"route_maps": ["RM_LC1"]}}
+ result = delete_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify route map is deleted")
+ result = verify_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "largeCommunity is still present after deleting route-map \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_duplicate_large_community_list_attributes_not_transitive(request):
+ """
+ This test is to verify that duplicate BGP Large Community values
+ are NOT be transmitted.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_1 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3"
+ " 2:0:4 2:0:5",
+ "action": "additive",
+ }
+ },
+ }
+ ],
+ "RM_R4_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "0:0:1 0:0:10 0:0:10000 2:0:1 2:0:2",
+ "action": "additive",
+ }
+ },
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r6"
+ input_dict_4 = {
+ "largeCommunity": "0:0:1 0:0:10 0:0:100 0:0:10000 2:0:1 2:0:2 2:0:3 2:0:4 2:0:5"
+ }
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_set_none(request):
+ """
+ This test is to verify if we want to remove all the large-community
+ attributes from a set of prefix we can set the value as NONE.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_1 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "0:0:1 0:0:10 0:0:100 2:0:1 2:0:2 2:0:3"
+ " 2:0:4",
+ "action": "additive",
+ }
+ },
+ }
+ ]
+ }
+ },
+ "r6": {
+ "route_maps": {
+ "RM_R6_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"large_community": {"num": "none"}},
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r6": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {
+ "route_maps": [
+ {"name": "RM_R6_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {
+ "route_maps": [
+ {"name": "RM_R6_IN", "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 Community-list")
+ dut = "r6"
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Community-list is still present \n Error: {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_lcomm_lists_with_redistribute_static_connected_rmap(request):
+ """
+ This test is to verify redistribute connected and static ipv4 routes
+ in BGP process with a route-map appending/removing L-comm attributes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("create static routes")
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "200.50.2.0/32", "next_hop": "10.0.0.6"},
+ {"network": "1::1/128", "next_hop": "fd00:0:0:1::2"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("redistribute static routes")
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": "route-map RM_R2_OUT",
+ },
+ {
+ "redist_type": "connected",
+ "attribute": "route-map RM_R2_OUT",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": "route-map RM_R2_OUT",
+ },
+ {
+ "redist_type": "connected",
+ "attribute": "route-map RM_R2_OUT",
+ },
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RM_R2_OUT": [
+ {
+ "action": "permit",
+ "set": {"large_community": {"num": "55:55:55 555:555:555"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list for static and connected ipv4 route on" " r2")
+
+ input_dict_5 = {"largeCommunity": "55:55:55 555:555:555"}
+
+ if "ipv4" in ADDR_TYPES:
+ dut = "r2"
+ networks = ["200.50.2.0/32", "1.0.1.17/32"]
+ result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify large-community-list for static and connected ipv4 route" " on r4")
+ dut = "r4"
+ networks = ["200.50.2.0/32", "1.0.1.17/32"]
+ result = verify_bgp_community(tgen, "ipv4", dut, networks, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ if "ipv6" in ADDR_TYPES:
+ step("Verify large-community-list for static and connected ipv6 route" " on r2")
+ dut = "r2"
+ networks = ["1::1/128", "2001:db8:f::1:17/128"]
+ result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify large-community-list for static and connected ipv6 route" " on r4")
+ dut = "r4"
+ networks = ["1::1/128", "2001:db8:f::1:17/128"]
+ result = verify_bgp_community(tgen, "ipv6", dut, networks, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_set_delete(request):
+ """
+ This test is to verify if we want to remove specific large-community
+ values from a set of prefix we can make use of DELETE operation based
+ on L-comm list
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("configure route_map")
+ input_dict_2 = {
+ "r6": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "Test",
+ "value": "1:2:1 1:1:10 1:3:100",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_3 = {
+ "r6": {
+ "route_maps": {
+ "RM_R6_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {"large_comm_list": {"id": "Test", "delete": True}},
+ }
+ ]
+ }
+ },
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "1:2:1 1:1:10 1:3:100 2:1:1 2:2:2 2:3:3"
+ " 2:4:4 2:5:5",
+ "action": "additive",
+ }
+ },
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ "r6": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {
+ "route_maps": [
+ {"name": "RM_R6_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {
+ "route_maps": [
+ {"name": "RM_R6_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r6"
+ input_dict_5 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_no_send_community(request):
+ """
+ This test is to verify if we want to remove specific large-community
+ values from a set of prefix we can make use of DELETE operation based
+ on L-comm list
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r5": {
+ "route_maps": {
+ "RM_R6_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r5": {
+ "route_maps": [
+ {
+ "name": "RM_R6_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {
+ "r5": {
+ "route_maps": [
+ {
+ "name": "RM_R6_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r6"
+ input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for no-send-community")
+ input_dict_5 = {
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {"r5": {"no_send_community": "large"}}
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r6": {
+ "dest_link": {"r5": {"no_send_community": "large"}}
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify Community-list")
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, adt, dut, NETWORKS[adt], input_dict_4, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_create_large_community_lists_with_no_attribute_values(request):
+ """
+ This test is to verify that large-community lists can not be
+ configured without providing specific L-community values
+ (for match/delete operation in a route-map).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create standard large commumity-list")
+ input_dict_1 = {
+ "r5": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "Test1",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_match_exact(request):
+ """
+ This test is to verify that Match_EXACT clause should pass
+ only if all of the L-comm values configured (horizontally)
+ in the community list is present in the prefix. There must
+ be no additional L-communities in the prefix.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r2": {
+ "route_maps": {
+ "RM_R4_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map and advertise networks")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create standard large commumity-list")
+ input_dict_4 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "EXACT",
+ "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_5 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {
+ "large-community-list": ["EXACT"],
+ "match_exact": True,
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_6 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r4"
+ input_dict_4 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_match_all(request):
+ """
+ This test is to verify that Match_ALL clause should pass
+ only if ALL of the L-comm values configured (horizontally)
+ in the community list are present in the prefix. There
+ could be additional L-communities in the prefix that are
+ not present in the L-comm list.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r2": {
+ "route_maps": {
+ "RM_R4_OUT": [
+ {
+ "action": "permit",
+ "set": {
+ "large_community": {
+ "num": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create standard large commumity-list")
+ input_dict_4 = {
+ "r3": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ALL",
+ "value": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_5 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"large-community-list": {"id": "ALL"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_6 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r4"
+ input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_match_any(request):
+ """
+ This test is to verify that Match_ANY clause should pass
+ only if at-least any one L-comm value configured(vertically)
+ in large-community list, is present in prefixes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r2": {
+ "route_maps": {
+ "RM_R4_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {"num": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create standard large commumity-list")
+ input_dict_4 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "2:1:1",
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "2:2:1",
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "2:3:1",
+ "large": True,
+ },
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ANY",
+ "value": "2:4:1",
+ "large": True,
+ },
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_5 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"large-community-list": {"id": "ANY"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_6 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r4"
+ input_dict_7 = {"largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_large_community_lists_with_rmap_match_regex(request):
+ """
+ This test is to verify large-community lists" operation in a route-map
+ with match RegEx statements. Match clause should pass only if the
+ complete string of L-comm values are matched
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ step("Create route map")
+ input_dict_2 = {
+ "r2": {
+ "route_maps": {
+ "RM_R4_OUT": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "set": {
+ "large_community": {
+ "num": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5",
+ },
+ "community": {"num": "1:1 1:2 1:3 1:4 1:5"},
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [{"network": "200.50.2.0/32"}]
+ }
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [
+ {
+ "name": "RM_R4_OUT",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create standard large commumity-list")
+ input_dict_4 = {
+ "r4": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "ALL",
+ "value": "1:1:1 2:1:3 2:1:4 2:1:5",
+ "large": True,
+ },
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "EXP_ALL",
+ "value": "1:1:1 2:1:[3-5]",
+ "large": True,
+ },
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP large community is created")
+ result = verify_create_community_list(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_5 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {
+ "large_community_list": {
+ "id": "ALL",
+ },
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map")
+ input_dict_6 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {"name": "RM_R4_IN", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r4"
+ input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(tgen, adt, dut, NETWORKS[adt], input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete route map reference by community-list")
+ input_dict_3 = {"r4": {"route_maps": ["RM_R4_IN"]}}
+ result = delete_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create route map")
+ input_dict_5 = {
+ "r4": {
+ "route_maps": {
+ "RM_R4_IN": [
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {
+ "large_community_list": {
+ "id": "EXP_ALL",
+ },
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("clear ip bgp")
+ result = clear_bgp_and_verify(tgen, topo, "r4")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify large-community-list")
+ dut = "r4"
+ input_dict_7 = {"largeCommunity": "1:1:1 1:1:2 2:1:3 2:1:4 2:1:5"}
+ for adt in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, adt, dut, NETWORKS[adt], input_dict_7, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "largeCommunity is still present \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/bgp_link_bw_ip/__init__.py b/tests/topotests/bgp_link_bw_ip/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/__init__.py
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json
new file mode 100644
index 0000000..3e3c35e
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-1.json
@@ -0,0 +1,19 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65301:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json
new file mode 100644
index 0000000..f07e89b
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-2.json
@@ -0,0 +1,32 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "extendedCommunity":{
+ "string":"LB:65303:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65201:375000 (3.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json
new file mode 100644
index 0000000..3501d12
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-3.json
@@ -0,0 +1,32 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "extendedCommunity":{
+ "string":"LB:65303:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65301:250000 (2.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json
new file mode 100644
index 0000000..b1ed004
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-4.json
@@ -0,0 +1,32 @@
+{
+ "prefix":"198.10.1.11\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "extendedCommunity":{
+ "string":"LB:65303:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65201:250000 (2.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json
new file mode 100644
index 0000000..89469b8
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgp-route-5.json
@@ -0,0 +1,29 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "nexthops":[
+ {
+ "ip":"11.1.1.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65201:375000 (3.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.1.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf
new file mode 100644
index 0000000..b1ec70d
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/bgpd.conf
@@ -0,0 +1,12 @@
+hostname r1
+!
+router bgp 65101
+ bgp router-id 11.1.1.1
+ no bgp ebgp-requires-policy
+ bgp bestpath as-path multipath-relax
+ neighbor 11.1.1.2 remote-as external
+ neighbor 11.1.1.2 timers 3 10
+ neighbor 11.1.1.6 remote-as external
+ neighbor 11.1.1.6 timers 3 10
+ neighbor 11.1.1.6 disable-link-bw-encoding-ieee
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json
new file mode 100644
index 0000000..3c02e26
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-1.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":25
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":75
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json
new file mode 100644
index 0000000..3c2d42c
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-2.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":33
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":66
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json
new file mode 100644
index 0000000..3d80018
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-3.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.11\/32":[
+ {
+ "prefix":"198.10.1.11\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":33
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":66
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json
new file mode 100644
index 0000000..6b757ef
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-4.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":1
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json
new file mode 100644
index 0000000..641ecab
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-5.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.11\/32":[
+ {
+ "prefix":"198.10.1.11\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":1
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json
new file mode 100644
index 0000000..6ed3f8e
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-6.json
@@ -0,0 +1,15 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":100
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json
new file mode 100644
index 0000000..95531d9
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-7.json
@@ -0,0 +1,15 @@
+{
+ "198.10.1.11\/32":[
+ {
+ "prefix":"198.10.1.11\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":100
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json
new file mode 100644
index 0000000..beac501
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-8.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":1
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":100
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json
new file mode 100644
index 0000000..eb27ce2
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/ip-route-9.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.11\/32":[
+ {
+ "prefix":"198.10.1.11\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.1.6",
+ "weight":1
+ },
+ {
+ "fib":true,
+ "ip":"11.1.1.2",
+ "weight":100
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/v4_route.json b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json
new file mode 100644
index 0000000..76c6396
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/v4_route.json
@@ -0,0 +1,84 @@
+{
+ "10.0.1.1\/32":[
+ {
+ "prefix":"10.0.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":9,
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.4\/32":[
+ {
+ "prefix":"10.0.3.4\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.20.1\/32":[
+ {
+ "prefix":"10.0.20.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":11,
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r1/zebra.conf b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf
new file mode 100644
index 0000000..0fc81f9
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0
+ ip address 11.1.1.1/30
+!
+interface r1-eth1
+ ip address 11.1.1.5/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf
new file mode 100644
index 0000000..022270f
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r10/bgpd.conf
@@ -0,0 +1,17 @@
+hostname r10
+!
+ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32
+!
+route-map redist permit 10
+ match ip address prefix-list redist
+!
+router bgp 65354
+ bgp router-id 11.1.6.2
+ no bgp ebgp-requires-policy
+ neighbor 11.1.6.1 remote-as external
+ neighbor 11.1.6.1 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected route-map redist
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r10/zebra.conf b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf
new file mode 100644
index 0000000..1a24fda
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r10/zebra.conf
@@ -0,0 +1,6 @@
+interface r10-eth0
+ ip address 11.1.6.2/30
+!
+interface r10-eth1
+ ip address 50.1.1.10/32
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json
new file mode 100644
index 0000000..3c38689
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-1.json
@@ -0,0 +1,19 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65301:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.2.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json
new file mode 100644
index 0000000..1895cd8
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-2.json
@@ -0,0 +1,19 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65301:250000 (2.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.2.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json
new file mode 100644
index 0000000..dfc4171
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/bgp-route-3.json
@@ -0,0 +1,32 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "extendedCommunity":{
+ "string":"LB:65302:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.2.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"LB:65301:250000 (2.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.2.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf
new file mode 100644
index 0000000..0c0e859
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/bgpd.conf
@@ -0,0 +1,13 @@
+hostname r2
+!
+router bgp 65201
+ bgp router-id 11.1.2.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 11.1.1.1 remote-as external
+ neighbor 11.1.1.1 timers 3 10
+ neighbor 11.1.2.2 remote-as external
+ neighbor 11.1.2.2 timers 3 10
+ neighbor 11.1.2.6 remote-as external
+ neighbor 11.1.2.6 timers 3 10
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json
new file mode 100644
index 0000000..131100a
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-1.json
@@ -0,0 +1,19 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.2.2",
+ "interfaceName":"r2-eth1",
+ "active":true,
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json
new file mode 100644
index 0000000..7e2fa6b
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-2.json
@@ -0,0 +1,20 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.2.6",
+ "weight":33
+ },
+ {
+ "fib":true,
+ "ip":"11.1.2.2",
+ "weight":66
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json
new file mode 100644
index 0000000..d0509bb
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/ip-route-3.json
@@ -0,0 +1,15 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.2.2",
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r2/zebra.conf b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf
new file mode 100644
index 0000000..23573a1
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r2/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface r2-eth0
+ ip address 11.1.1.2/30
+!
+interface r2-eth1
+ ip address 11.1.2.1/30
+!
+interface r2-eth2
+ ip address 11.1.2.5/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json
new file mode 100644
index 0000000..cddf127
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r3/bgp-route-1.json
@@ -0,0 +1,29 @@
+{
+ "prefix":"198.10.1.1/32",
+ "paths":[
+ {
+ "aspath":{
+ "string":"65303 65354",
+ "segments":[
+ {
+ "type":"as-sequence",
+ "list":[
+ 65303,
+ 65354
+ ]
+ }
+ ],
+ "length":2
+ },
+ "valid":true,
+ "extendedCommunity":{
+ "string":"LB:65303:125000 (1.000 Mbps)"
+ },
+ "nexthops":[
+ {
+ "ip":"11.1.3.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf
new file mode 100644
index 0000000..cfd3949
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r3/bgpd.conf
@@ -0,0 +1,12 @@
+hostname r3
+!
+router bgp 65202
+ bgp router-id 11.1.3.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 11.1.1.5 remote-as external
+ neighbor 11.1.1.5 timers 3 10
+ neighbor 11.1.3.2 remote-as external
+ neighbor 11.1.3.2 timers 3 10
+ neighbor 11.1.3.2 disable-link-bw-encoding-ieee
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r3/zebra.conf b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf
new file mode 100644
index 0000000..d667669
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r3-eth0
+ ip address 11.1.1.6/30
+!
+interface r3-eth1
+ ip address 11.1.3.1/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json
new file mode 100644
index 0000000..87d1ae0
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r4/bgp-route-1.json
@@ -0,0 +1,23 @@
+{
+ "prefix":"198.10.1.1\/32",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "nexthops":[
+ {
+ "ip":"11.1.4.6"
+ }
+ ]
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "nexthops":[
+ {
+ "ip":"11.1.4.2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf
new file mode 100644
index 0000000..88b260f
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r4/bgpd.conf
@@ -0,0 +1,32 @@
+!
+log file bgpd.log
+!
+! debug bgp updates
+! debug bgp zebra
+! debug bgp bestpath 198.10.1.1/32
+!
+hostname r4
+!
+ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32
+!
+route-map anycast_ip permit 10
+ match ip address prefix-list anycast_ip
+ set extcommunity bandwidth num-multipaths
+!
+route-map anycast_ip permit 20
+!
+router bgp 65301
+ bgp router-id 11.1.4.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 11.1.2.1 remote-as external
+ neighbor 11.1.2.1 timers 3 10
+ neighbor 11.1.4.2 remote-as external
+ neighbor 11.1.4.2 timers 3 10
+ neighbor 11.1.4.6 remote-as external
+ neighbor 11.1.4.6 timers 3 10
+ !
+ address-family ipv4 unicast
+ neighbor 11.1.2.1 route-map anycast_ip out
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json
new file mode 100644
index 0000000..a9ccf07
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r4/ip-route-1.json
@@ -0,0 +1,21 @@
+{
+ "198.10.1.1\/32":[
+ {
+ "prefix":"198.10.1.1\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"11.1.4.2",
+ "weight":1
+ },
+ {
+ "fib":true,
+ "ip":"11.1.4.6",
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_link_bw_ip/r4/zebra.conf b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf
new file mode 100644
index 0000000..ef61f7e
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r4/zebra.conf
@@ -0,0 +1,10 @@
+!
+interface r4-eth0
+ ip address 11.1.2.2/30
+!
+interface r4-eth1
+ ip address 11.1.4.1/30
+!
+interface r4-eth2
+ ip address 11.1.4.5/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf
new file mode 100644
index 0000000..4014bfb
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r5/bgpd.conf
@@ -0,0 +1,23 @@
+hostname r5
+!
+ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32
+!
+route-map anycast_ip permit 10
+ match ip address prefix-list anycast_ip
+ set extcommunity bandwidth num-multipaths
+!
+route-map anycast_ip permit 20
+!
+router bgp 65302
+ bgp router-id 11.1.5.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 11.1.2.5 remote-as external
+ neighbor 11.1.2.5 timers 3 10
+ neighbor 11.1.5.2 remote-as external
+ neighbor 11.1.5.2 timers 3 10
+ !
+ address-family ipv4 unicast
+ neighbor 11.1.2.5 route-map anycast_ip out
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r5/zebra.conf b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf
new file mode 100644
index 0000000..66c6596
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r5/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r5-eth0
+ ip address 11.1.2.6/30
+!
+interface r5-eth1
+ ip address 11.1.5.1/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf
new file mode 100644
index 0000000..89de8ee
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r6/bgpd.conf
@@ -0,0 +1,24 @@
+hostname r6
+!
+ip prefix-list anycast_ip seq 10 permit 198.10.1.0/24 le 32
+!
+route-map anycast_ip permit 10
+ match ip address prefix-list anycast_ip
+ set extcommunity bandwidth num-multipaths
+!
+route-map anycast_ip permit 20
+!
+router bgp 65303
+ bgp router-id 11.1.6.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 11.1.3.1 remote-as external
+ neighbor 11.1.3.1 timers 3 10
+ neighbor 11.1.3.1 disable-link-bw-encoding-ieee
+ neighbor 11.1.6.2 remote-as external
+ neighbor 11.1.6.2 timers 3 10
+ !
+ address-family ipv4 unicast
+ neighbor 11.1.3.1 route-map anycast_ip out
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r6/zebra.conf b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf
new file mode 100644
index 0000000..66ff563
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r6/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r6-eth0
+ ip address 11.1.3.2/30
+!
+interface r6-eth1
+ ip address 11.1.6.1/30
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf
new file mode 100644
index 0000000..39c0c81
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r7/bgpd.conf
@@ -0,0 +1,17 @@
+hostname r7
+!
+ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32
+!
+route-map redist permit 10
+ match ip address prefix-list redist
+!
+router bgp 65351
+ bgp router-id 11.1.4.2
+ no bgp ebgp-requires-policy
+ neighbor 11.1.4.1 remote-as external
+ neighbor 11.1.4.1 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected route-map redist
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r7/zebra.conf b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf
new file mode 100644
index 0000000..38e36ca
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r7/zebra.conf
@@ -0,0 +1,6 @@
+interface r7-eth0
+ ip address 11.1.4.2/30
+!
+interface r7-eth1
+ ip address 50.1.1.7/32
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf
new file mode 100644
index 0000000..ea1d546
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r8/bgpd.conf
@@ -0,0 +1,17 @@
+hostname r8
+!
+ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32
+!
+route-map redist permit 10
+ match ip address prefix-list redist
+!
+router bgp 65352
+ bgp router-id 11.1.4.6
+ no bgp ebgp-requires-policy
+ neighbor 11.1.4.5 remote-as external
+ neighbor 11.1.4.5 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected route-map redist
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r8/zebra.conf b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf
new file mode 100644
index 0000000..1369e19
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r8/zebra.conf
@@ -0,0 +1,6 @@
+interface r8-eth0
+ ip address 11.1.4.6/30
+!
+interface r8-eth1
+ ip address 50.1.1.8/32
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf
new file mode 100644
index 0000000..a6066ee
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r9/bgpd.conf
@@ -0,0 +1,17 @@
+hostname r9
+!
+ip prefix-list redist seq 10 permit 0.0.0.0/0 ge 32
+!
+route-map redist permit 10
+ match ip address prefix-list redist
+!
+router bgp 65353
+ bgp router-id 11.1.5.2
+ no bgp ebgp-requires-policy
+ neighbor 11.1.5.1 remote-as external
+ neighbor 11.1.5.1 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected route-map redist
+ !
+!
diff --git a/tests/topotests/bgp_link_bw_ip/r9/zebra.conf b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf
new file mode 100644
index 0000000..c73caf3
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/r9/zebra.conf
@@ -0,0 +1,6 @@
+interface r9-eth0
+ ip address 11.1.5.2/30
+!
+interface r9-eth1
+ ip address 50.1.1.9/32
+!
diff --git a/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
new file mode 100644
index 0000000..4214f3a
--- /dev/null
+++ b/tests/topotests/bgp_link_bw_ip/test_bgp_linkbw_ip.py
@@ -0,0 +1,588 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_linkbw_ip.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc
+# Vivek Venkatraman
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_linkbw_ip.py: Test weighted ECMP using BGP link-bandwidth
+"""
+
+import os
+import sys
+from functools import partial
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd]
+
+
+"""
+This topology is for validating one of the primary use cases for
+weighted ECMP (a.k.a. Unequal cost multipath) using BGP link-bandwidth:
+https://tools.ietf.org/html/draft-mohanty-bess-ebgp-dmz
+
+The topology consists of two PODs. Pod-1 consists of a spine switch
+and two leaf switches, with two servers attached to the first leaf and
+one to the second leaf. Pod-2 consists of one spine and one leaf, with
+one server connected to the leaf. The PODs are connected by a super-spine
+switch.
+
+Note that the use of the term "switch" above is in keeping with common
+data-center terminology. These devices are all regular routers; for
+this scenario, the servers are also routers as they have to announce
+anycast IP (VIP) addresses via BGP.
+"""
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 10 routers - 1 super-spine, 2 spines, 3 leafs
+ # and 4 servers
+ routers = {}
+ for i in range(1, 11):
+ routers[i] = tgen.add_router("r{}".format(i))
+
+ # Create 13 "switches" - to interconnect the above routers
+ switches = {}
+ for i in range(1, 14):
+ switches[i] = tgen.add_switch("s{}".format(i))
+
+ # Interconnect R1 (super-spine) to R2 and R3 (the two spines)
+ switches[1].add_link(tgen.gears["r1"])
+ switches[1].add_link(tgen.gears["r2"])
+ switches[2].add_link(tgen.gears["r1"])
+ switches[2].add_link(tgen.gears["r3"])
+
+ # Interconnect R2 (spine in pod-1) to R4 and R5 (the associated
+ # leaf switches)
+ switches[3].add_link(tgen.gears["r2"])
+ switches[3].add_link(tgen.gears["r4"])
+ switches[4].add_link(tgen.gears["r2"])
+ switches[4].add_link(tgen.gears["r5"])
+
+ # Interconnect R3 (spine in pod-2) to R6 (associated leaf)
+ switches[5].add_link(tgen.gears["r3"])
+ switches[5].add_link(tgen.gears["r6"])
+
+ # Interconnect leaf switches to servers
+ switches[6].add_link(tgen.gears["r4"])
+ switches[6].add_link(tgen.gears["r7"])
+ switches[7].add_link(tgen.gears["r4"])
+ switches[7].add_link(tgen.gears["r8"])
+ switches[8].add_link(tgen.gears["r5"])
+ switches[8].add_link(tgen.gears["r9"])
+ switches[9].add_link(tgen.gears["r6"])
+ switches[9].add_link(tgen.gears["r10"])
+
+ # Create empty networks for the servers
+ switches[10].add_link(tgen.gears["r7"])
+ switches[11].add_link(tgen.gears["r8"])
+ switches[12].add_link(tgen.gears["r9"])
+ switches[13].add_link(tgen.gears["r10"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_linkbw_adv():
+ "Test #1: Test BGP link-bandwidth advertisement based on number of multipaths"
+ logger.info(
+ "\nTest #1: Test BGP link-bandwidth advertisement based on number of multipaths"
+ )
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ # Configure anycast IP on server r7
+ logger.info("Configure anycast IP on server r7")
+
+ tgen.net["r7"].cmd("ip addr add 198.10.1.1/32 dev r7-eth1")
+
+ # Check on spine router r2 for link-bw advertisement by leaf router r4
+ logger.info("Check on spine router r2 for link-bw advertisement by leaf router r4")
+
+ json_file = "{}/r2/bgp-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+ # Check on spine router r2 that default weight is used as there is no multipath
+ logger.info(
+ "Check on spine router r2 that default weight is used as there is no multipath"
+ )
+
+ json_file = "{}/r2/ip-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+ # Check on super-spine router r1 that link-bw has been propagated by spine router r2
+ logger.info(
+ "Check on super-spine router r1 that link-bw has been propagated by spine router r2"
+ )
+
+ json_file = "{}/r1/bgp-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+def test_bgp_cumul_linkbw():
+ "Test #2: Test cumulative link-bandwidth propagation"
+ logger.info("\nTest #2: Test cumulative link-bandwidth propagation")
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r4 = tgen.gears["r4"]
+
+ # Configure anycast IP on additional server r8
+ logger.info("Configure anycast IP on server r8")
+
+ tgen.net["r8"].cmd("ip addr add 198.10.1.1/32 dev r8-eth1")
+
+ # Check multipath on leaf router r4
+ logger.info("Check multipath on leaf router r4")
+
+ json_file = "{}/r4/bgp-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r4, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on leaf router r4"
+ assert result is None, assertmsg
+
+ # Check regular ECMP is in effect on leaf router r4
+ logger.info("Check regular ECMP is in effect on leaf router r4")
+
+ json_file = "{}/r4/ip-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r4, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on leaf router r4"
+ assert result is None, assertmsg
+
+ # Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths
+ logger.info(
+ "Check on spine router r2 that leaf has propagated the cumulative link-bw based on num-multipaths"
+ )
+
+ json_file = "{}/r2/bgp-route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+
+def test_weighted_ecmp():
+ "Test #3: Test weighted ECMP - multipath with next hop weights"
+ logger.info("\nTest #3: Test weighted ECMP - multipath with next hop weights")
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ # Configure anycast IP on additional server r9
+ logger.info("Configure anycast IP on server r9")
+
+ tgen.net["r9"].cmd("ip addr add 198.10.1.1/32 dev r9-eth1")
+
+ # Check multipath on spine router r2
+ logger.info("Check multipath on spine router r2")
+ json_file = "{}/r2/bgp-route-3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+ # Check weighted ECMP is in effect on the spine router r2
+ logger.info("Check weighted ECMP is in effect on the spine router r2")
+
+ json_file = "{}/r2/ip-route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+ # Configure anycast IP on additional server r10
+ logger.info("Configure anycast IP on server r10")
+
+ tgen.net["r10"].cmd("ip addr add 198.10.1.1/32 dev r10-eth1")
+
+ # Check if bandwidth is properly encoded with non IEEE floatig-point (uint32) format on r3
+ logger.info(
+ "Check if bandwidth is properly encoded with non IEEE floatig-point (uint32) format on r3"
+ )
+ json_file = "{}/r3/bgp-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on r3"
+ assert result is None, assertmsg
+
+ # Check multipath on super-spine router r1
+ logger.info("Check multipath on super-spine router r1")
+ json_file = "{}/r1/bgp-route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ # Check weighted ECMP is in effect on the super-spine router r1
+ logger.info("Check weighted ECMP is in effect on the super-spine router r1")
+ json_file = "{}/r1/ip-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+def test_weighted_ecmp_link_flap():
+ "Test #4: Test weighted ECMP rebalancing upon change (link flap)"
+ logger.info("\nTest #4: Test weighted ECMP rebalancing upon change (link flap)")
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ # Bring down link on server r9
+ logger.info("Bring down link on server r9")
+
+ tgen.net["r9"].cmd("ip link set dev r9-eth1 down")
+
+ # Check spine router r2 has only one path
+ logger.info("Check spine router r2 has only one path")
+
+ json_file = "{}/r2/ip-route-3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on spine router r2"
+ assert result is None, assertmsg
+
+ # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1
+ logger.info(
+ "Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1"
+ )
+
+ json_file = "{}/r1/bgp-route-3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ # Bring up link on server r9
+ logger.info("Bring up link on server r9")
+
+ tgen.net["r9"].cmd("ip link set dev r9-eth1 up")
+
+ # Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1
+ logger.info(
+ "Check link-bandwidth change and weighted ECMP rebalance on super-spine router r1"
+ )
+
+ json_file = "{}/r1/bgp-route-2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+def test_weighted_ecmp_second_anycast_ip():
+ "Test #5: Test weighted ECMP for a second anycast IP"
+ logger.info("\nTest #5: Test weighted ECMP for a second anycast IP")
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ # Configure anycast IP on additional server r7, r9 and r10
+ logger.info("Configure anycast IP on server r7, r9 and r10")
+
+ tgen.net["r7"].cmd("ip addr add 198.10.1.11/32 dev r7-eth1")
+ tgen.net["r9"].cmd("ip addr add 198.10.1.11/32 dev r9-eth1")
+ tgen.net["r10"].cmd("ip addr add 198.10.1.11/32 dev r10-eth1")
+
+ # Check link-bandwidth and weighted ECMP on super-spine router r1
+ logger.info("Check link-bandwidth and weighted ECMP on super-spine router r1")
+
+ json_file = "{}/r1/bgp-route-4.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.11/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+def test_paths_with_and_without_linkbw():
+ "Test #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP"
+ logger.info(
+ "\nTest #6: Test paths with and without link-bandwidth - receiver should resort to regular ECMP"
+ )
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+
+ # Configure leaf router r6 to not advertise any link-bandwidth
+ logger.info("Configure leaf router r6 to not advertise any link-bandwidth")
+
+ tgen.net["r6"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65303" -c "address-family ipv4 unicast" -c "no neighbor 11.1.3.1 route-map anycast_ip out"'
+ )
+
+ # Check link-bandwidth change on super-spine router r1
+ logger.info("Check link-bandwidth change on super-spine router r1")
+
+ json_file = "{}/r1/bgp-route-5.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show bgp ipv4 uni 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ # Check super-spine router r1 resorts to regular ECMP
+ logger.info("Check super-spine router r1 resorts to regular ECMP")
+
+ json_file = "{}/r1/ip-route-4.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-5.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=50, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+def test_linkbw_handling_options():
+ "Test #7: Test different options for processing link-bandwidth on the receiver"
+ logger.info(
+ "\nTest #7: Test different options for processing link-bandwidth on the receiver"
+ )
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+
+ # Configure super-spine r1 to skip multipaths without link-bandwidth
+ logger.info("Configure super-spine r1 to skip multipaths without link-bandwidth")
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65101" -c "bgp bestpath bandwidth skip-missing"'
+ )
+
+ # Check super-spine router r1 resorts to only one path as other path is skipped
+ logger.info(
+ "Check super-spine router r1 resorts to only one path as other path is skipped"
+ )
+
+ json_file = "{}/r1/ip-route-6.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-7.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ # Configure super-spine r1 to use default-weight for multipaths without link-bandwidth
+ logger.info(
+ "Configure super-spine r1 to use default-weight for multipaths without link-bandwidth"
+ )
+
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router bgp 65101" -c "bgp bestpath bandwidth default-weight-for-missing"'
+ )
+
+ # Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth
+ logger.info(
+ "Check super-spine router r1 uses ECMP with weight 1 for path without link-bandwidth"
+ )
+
+ json_file = "{}/r1/ip-route-8.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.1/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+ json_file = "{}/r1/ip-route-9.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 198.10.1.11/32 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=200, wait=0.5)
+ assertmsg = "JSON output mismatch on super-spine router r1"
+ assert result is None, assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json
new file mode 100644
index 0000000..95de8cc
--- /dev/null
+++ b/tests/topotests/bgp_listen_on_multiple_addresses/bgp_listen_on_multiple_addresses.json
@@ -0,0 +1,154 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:DB8:F::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "1000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "2000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "2000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "3000",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
new file mode 100755
index 0000000..4b4335a
--- /dev/null
+++ b/tests/topotests/bgp_listen_on_multiple_addresses/test_bgp_listen_on_multiple_addresses.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_listen_on_multiple_addresses.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Boeing Defence Australia
+# Adriano Marto Reis
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_listen_on_multiple_addresses.py: Test BGP daemon listening for
+connections on multiple addresses.
+
+ +------+ +------+ +------+ +------+
+ | | | | | | | |
+ | r1 |--------| r2 |--------| r3 |--------| r4 |
+ | | | | | | | |
+ +------+ +------+ +------+ +------+
+
+ | | | |
+ | AS 1000 | AS 2000 | AS 3000 |
+ | | | |
+ +------------+--------------------------------+-------------+
+"""
+
+import os
+import sys
+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, "../"))
+
+from lib.topogen import Topogen, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topojson import linux_intf_config_from_json
+from lib.common_config import start_topology
+from lib.topotest import router_json_cmp, run_and_expect
+from functools import partial
+
+pytestmark = [pytest.mark.bgpd]
+
+
+LISTEN_ADDRESSES = {
+ "r1": ["10.0.0.1"],
+ "r2": ["10.0.0.2", "10.0.1.1"],
+ "r3": ["10.0.1.2", "10.0.2.1"],
+ "r4": ["10.0.2.2"],
+}
+
+
+def setup_module(mod):
+ "Sets up the test environment."
+ json_file = "{}/bgp_listen_on_multiple_addresses.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+
+ # Adds extra parameters to bgpd so they listen for connections on specific
+ # multiple addresses.
+ for router_name in tgen.routers().keys():
+ tgen.net[router_name].daemons_options["bgpd"] = "-l " + " -l ".join(
+ LISTEN_ADDRESSES[router_name]
+ )
+
+ start_topology(tgen)
+
+ linux_intf_config_from_json(tgen, topo)
+
+ build_config_from_json(tgen, topo)
+
+
+def teardown_module(_mod):
+ "Tears-down the test environment."
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_peering():
+ "Checks if the routers peer-up."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ _bgp_converge_initial("r1", "10.0.0.2")
+ _bgp_converge_initial("r2", "10.0.0.1")
+ _bgp_converge_initial("r2", "10.0.1.2")
+ _bgp_converge_initial("r3", "10.0.1.1")
+ _bgp_converge_initial("r3", "10.0.2.2")
+ _bgp_converge_initial("r4", "10.0.2.1")
+
+
+def test_listening_address():
+ """
+ Checks if bgpd is only listening on the specified IP addresses.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for router in tgen.routers().values():
+ # bgpd must not be listening on the default address.
+ output = router.run("netstat -nlt4 | grep 0.0.0.0:179")
+ assert output == "", "{}: bpgd is listening on 0.0.0.0:179".format(router.name)
+
+ # bgpd must be listening on the specified addresses.
+ for address in LISTEN_ADDRESSES[router.name]:
+ output = router.run("netstat -nlt4 | grep {}:179".format(address))
+ assert output != "", "{}: bpgd is not listening on {}:179".format(
+ router.name, address
+ )
+
+
+def _bgp_converge_initial(router_name, peer_address, timeout=180):
+ """
+ Waits for the BGP connection between a given router and a given peer
+ (specified by its IP address) to be established. If the connection is
+ not established within a given timeout, then an exception is raised.
+ """
+ tgen = get_topogen()
+ router = tgen.routers()[router_name]
+ expected = {"ipv4Unicast": {"peers": {peer_address: {"state": "Established"}}}}
+
+ test_func = partial(router_json_cmp, router, "show ip bgp summary json", expected)
+ _, result = run_and_expect(test_func, None, count=timeout, wait=1)
+ assert result is None, "{}: Failed to establish connection with {}".format(
+ router_name, peer_address
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_llgr/__init__.py b/tests/topotests/bgp_llgr/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_llgr/__init__.py
diff --git a/tests/topotests/bgp_llgr/r0/bgpd.conf b/tests/topotests/bgp_llgr/r0/bgpd.conf
new file mode 100644
index 0000000..a20e64f
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r0/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp graceful-restart restart-time 0
+ bgp long-lived-graceful-restart stale-time 20
+ neighbor 192.168.0.2 remote-as external
+ neighbor 192.168.0.2 timers 3 10
+ neighbor 192.168.0.2 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_llgr/r0/zebra.conf b/tests/topotests/bgp_llgr/r0/zebra.conf
new file mode 100644
index 0000000..4d11b67
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r0/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.1.1/32
+!
+int r0-eth0
+ ip address 192.168.0.1/24
+!
diff --git a/tests/topotests/bgp_llgr/r1/bgpd.conf b/tests/topotests/bgp_llgr/r1/bgpd.conf
new file mode 100644
index 0000000..bf33eeb
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r1/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp graceful-restart restart-time 0
+ bgp long-lived-graceful-restart stale-time 20
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 192.168.1.2 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_llgr/r1/zebra.conf b/tests/topotests/bgp_llgr/r1/zebra.conf
new file mode 100644
index 0000000..1782edc
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.1.1/32
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_llgr/r2/bgpd.conf b/tests/topotests/bgp_llgr/r2/bgpd.conf
new file mode 100644
index 0000000..6515ef8
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp long-lived-graceful-restart stale-time 20
+ neighbor 192.168.0.1 remote-as external
+ neighbor 192.168.0.1 timers 3 10
+ neighbor 192.168.0.1 timers connect 1
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.1.1 timers connect 1
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.2.1 timers connect 1
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ bgp listen range 192.168.3.0/24 peer-group PG
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 weight 100
diff --git a/tests/topotests/bgp_llgr/r2/zebra.conf b/tests/topotests/bgp_llgr/r2/zebra.conf
new file mode 100644
index 0000000..e5e88d4
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r2/zebra.conf
@@ -0,0 +1,13 @@
+!
+int r2-eth0
+ ip address 192.168.0.2/24
+!
+int r2-eth1
+ ip address 192.168.1.2/24
+!
+int r2-eth2
+ ip address 192.168.2.2/24
+!
+int r2-eth3
+ ip address 192.168.3.2/24
+!
diff --git a/tests/topotests/bgp_llgr/r3/bgpd.conf b/tests/topotests/bgp_llgr/r3/bgpd.conf
new file mode 100644
index 0000000..dfe20c1
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r3/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65003
+ bgp router-id 192.168.2.1
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp long-lived-graceful-restart stale-time 20
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 3 10
+ neighbor 192.168.2.2 timers connect 1
diff --git a/tests/topotests/bgp_llgr/r3/zebra.conf b/tests/topotests/bgp_llgr/r3/zebra.conf
new file mode 100644
index 0000000..8a7941b
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r3/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r3-eth0
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/bgp_llgr/r4/bgpd.conf b/tests/topotests/bgp_llgr/r4/bgpd.conf
new file mode 100644
index 0000000..49ce387
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r4/bgpd.conf
@@ -0,0 +1,13 @@
+router bgp 65004
+ bgp router-id 192.168.3.1
+ no bgp ebgp-requires-policy
+ bgp graceful-restart
+ bgp graceful-restart restart-time 0
+ bgp long-lived-graceful-restart stale-time 30
+ neighbor 192.168.3.2 remote-as external
+ neighbor 192.168.3.2 timers 3 10
+ neighbor 192.168.3.2 timers connect 1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_llgr/r4/zebra.conf b/tests/topotests/bgp_llgr/r4/zebra.conf
new file mode 100644
index 0000000..7ec20be
--- /dev/null
+++ b/tests/topotests/bgp_llgr/r4/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.1.2/32
+!
+int r4-eth0
+ ip address 192.168.3.1/24
+!
diff --git a/tests/topotests/bgp_llgr/test_bgp_llgr.py b/tests/topotests/bgp_llgr/test_bgp_llgr.py
new file mode 100644
index 0000000..fa57f66
--- /dev/null
+++ b/tests/topotests/bgp_llgr/test_bgp_llgr.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if BGP Long-lived Graceful Restart capability works:
+ Check if we can see 172.16.1.1/32 after initial converge in R3.
+ Check if we can see 172.16.1.1/32 as best selected due to higher weigth in R2.
+ Kill bgpd in R1.
+ Check if we can see 172.16.1.1/32 as stale in R2.
+ Check if we can see 172.16.1.1/32 depreferenced due to LLGR_STALE in R2.
+ Check if we can see 172.16.1.1/32 after R1 was killed in R3.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+from lib.common_config import (
+ kill_router_daemons,
+ start_router_daemons,
+ step,
+)
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(0, 6):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s0")
+ switch.add_link(tgen.gears["r0"])
+ switch.add_link(tgen.gears["r2"])
+
+ 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"])
+
+ # Dynamic neighbor
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_llgr():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp json"))
+ expected = {
+ "routes": {
+ "172.16.1.1/32": [{"nexthops": [{"ip": "192.168.2.2", "used": True}]}],
+ "172.16.1.2/32": [{"nexthops": [{"ip": "192.168.2.2", "used": True}]}],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check if we can see 172.16.1.1/32 after initial converge in R3")
+ test_func = functools.partial(_bgp_converge, r3)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see 172.16.1.1/32 in r3"
+
+ def _bgp_weight_prefered_route(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "bestpath": {"selectionReason": "Weight"},
+ "nexthops": [
+ {
+ "ip": "192.168.1.1",
+ }
+ ],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step(
+ "Check if we can see 172.16.1.1/32 as best selected due to higher weigth in R2"
+ )
+ test_func = functools.partial(_bgp_weight_prefered_route, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert (
+ result is None
+ ), "Prefix 172.16.1.1/32 is not selected as bests path due to weight"
+
+ step("Kill bgpd in R1")
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ def _bgp_stale_route(router, prefix):
+ output = json.loads(router.vtysh_cmd("show ip bgp {} json".format(prefix)))
+ expected = {"paths": [{"community": {"string": "llgr-stale"}, "stale": True}]}
+ return topotest.json_cmp(output, expected)
+
+ step("Check if we can see 172.16.1.1/32 as stale in R2")
+ test_func = functools.partial(_bgp_stale_route, r2, "172.16.1.1/32")
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Prefix 172.16.1.1/32 is not stale"
+
+ def _bgp_llgr_depreference_route(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.1.1/32 json"))
+ expected = {
+ "paths": [
+ {
+ "bestpath": {"selectionReason": "First path received"},
+ "nexthops": [
+ {
+ "ip": "192.168.0.1",
+ }
+ ],
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ step("Check if we can see 172.16.1.1/32 depreferenced due to LLGR_STALE in R2")
+ test_func = functools.partial(_bgp_llgr_depreference_route, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Prefix 172.16.1.1/32 is not depreferenced due to LLGR_STALE"
+
+ step("Check if we can see 172.16.1.1/32 after R1 was killed in R3")
+ test_func = functools.partial(_bgp_converge, r3)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see 172.16.1.1/32 in r3"
+
+ step("Kill bgpd in R4 (dynamic peer)")
+ kill_router_daemons(tgen, "r4", ["bgpd"])
+
+ step("Check if we can see 172.16.1.2/32 after R4 (dynamic peer) was killed")
+ test_func = functools.partial(_bgp_stale_route, r2, "172.16.1.2/32")
+ _, result = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assert result is None, "Cannot see 172.16.1.2/32 in r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_local_as_private_remove/__init__.py b/tests/topotests/bgp_local_as_private_remove/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/__init__.py
diff --git a/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf
new file mode 100644
index 0000000..16a9eea
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 remove-private-AS
+ redistribute connected
diff --git a/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf
new file mode 100644
index 0000000..802c327
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 1000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf
new file mode 100644
index 0000000..9a83127
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r3/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 3000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 1000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 local-as 500
+ address-family ipv4 unicast
+ neighbor 192.168.255.2 remove-private-AS
+ redistribute connected
diff --git a/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf
new file mode 100644
index 0000000..39499a1
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r3-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf
new file mode 100644
index 0000000..802c327
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r4/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 1000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 500
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf b/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf
new file mode 100644
index 0000000..b859115
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
new file mode 100644
index 0000000..bb2c43d
--- /dev/null
+++ b/tests/topotests/bgp_local_as_private_remove/test_bgp_local_as_private_remove.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python
+
+#
+# bgp_local_as_private_remove.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+bgp_local_as_private_remove.py:
+Test if primary AS number is not removed in cases when `local-as`
+used together with `remove-private-AS`.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_remove_private_as():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ while True:
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
+ if output["192.168.255.1"]["bgpState"] == "Established":
+ time.sleep(1)
+ return True
+
+ def _bgp_as_path(router):
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp 172.16.255.254/32 json")
+ )
+ if output["prefix"] == "172.16.255.254/32":
+ return output["paths"][0]["aspath"]["segments"][0]["list"]
+
+ if _bgp_converge("r2"):
+ assert len(_bgp_as_path("r2")) == 1
+ assert 65000 not in _bgp_as_path("r2")
+
+ if _bgp_converge("r4"):
+ assert len(_bgp_as_path("r4")) == 2
+ assert 3000 in _bgp_as_path("r4")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_lu_topo1/R1/bgpd.conf b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf
new file mode 100644
index 0000000..a164152
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/bgpd.conf
@@ -0,0 +1,21 @@
+!
+! debug bgp labelpool
+! debug bgp zebra
+!
+router bgp 1
+ bgp router-id 10.0.0.1
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.2 remote-as 2
+ neighbor 10.0.0.2 solo
+ neighbor 10.0.0.2 timers connect 10
+!
+ address-family ipv4 unicast
+ no neighbor 10.0.0.2 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.2 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
new file mode 100644
index 0000000..e2eee51
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":506,
+ "InUse":506,
+ "Requests":0,
+ "LabelChunks":3,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R1/zebra.conf b/tests/topotests/bgp_lu_topo1/R1/zebra.conf
new file mode 100644
index 0000000..f8a9ce4
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R1/zebra.conf
@@ -0,0 +1,6 @@
+! debug zebra events
+! debug zebra dplane
+! debug zebra mpls
+!
+interface R1-eth0
+ ip address 10.0.0.1/24
diff --git a/tests/topotests/bgp_lu_topo1/R2/bgpd.conf b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf
new file mode 100644
index 0000000..d35bbb8
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/bgpd.conf
@@ -0,0 +1,23 @@
+! debug bgp labelpool
+! debug bgp zebra
+!
+router bgp 2
+ bgp router-id 10.0.0.2
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.3 remote-as 2
+ neighbor 10.0.1.3 update-source 10.0.1.2
+ neighbor 10.0.1.3 timers connect 10
+ neighbor 10.0.0.1 remote-as 1
+ neighbor 10.0.0.1 timers connect 10
+!
+ address-family ipv4 unicast
+ neighbor 10.0.1.3 activate
+ no neighbor 10.0.0.1 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
new file mode 100644
index 0000000..9c817e8
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":0,
+ "InUse":0,
+ "Requests":0,
+ "LabelChunks":0,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo1/R2/zebra.conf b/tests/topotests/bgp_lu_topo1/R2/zebra.conf
new file mode 100644
index 0000000..083da3e
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R2/zebra.conf
@@ -0,0 +1,11 @@
+!
+! debug zebra events
+! debug zebra dplane
+! debug zebra mpls
+!
+interface R2-eth0
+ ip address 10.0.0.2/24
+!
+interface R2-eth1
+ ip address 10.0.1.2/24
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_lu_topo1/R3/bgpd.conf b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf
new file mode 100644
index 0000000..31d26ea
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R3/bgpd.conf
@@ -0,0 +1,523 @@
+log file /tmp/bgpd.log
+!
+! debug bgp updates
+!
+router bgp 2
+ bgp router-id 10.0.1.3
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.1.2 remote-as 2
+ neighbor 10.0.1.2 timers connect 10
+ !
+ address-family ipv4 unicast
+ neighbor 10.0.1.2 activate
+ network 11.0.0.1/32
+ network 11.0.0.2/32
+ network 11.0.0.3/32
+ network 11.0.0.4/32
+ network 11.0.0.5/32
+ network 11.0.0.6/32
+ network 11.0.0.7/32
+ network 11.0.0.8/32
+ network 11.0.0.9/32
+ network 11.0.0.10/32
+ network 11.0.0.11/32
+ network 11.0.0.12/32
+ network 11.0.0.13/32
+ network 11.0.0.14/32
+ network 11.0.0.15/32
+ network 11.0.0.16/32
+ network 11.0.0.17/32
+ network 11.0.0.18/32
+ network 11.0.0.19/32
+ network 11.0.0.20/32
+ network 11.0.0.21/32
+ network 11.0.0.22/32
+ network 11.0.0.23/32
+ network 11.0.0.24/32
+ network 11.0.0.25/32
+ network 11.0.0.26/32
+ network 11.0.0.27/32
+ network 11.0.0.28/32
+ network 11.0.0.29/32
+ network 11.0.0.30/32
+ network 11.0.0.31/32
+ network 11.0.0.32/32
+ network 11.0.0.33/32
+ network 11.0.0.34/32
+ network 11.0.0.35/32
+ network 11.0.0.36/32
+ network 11.0.0.37/32
+ network 11.0.0.38/32
+ network 11.0.0.39/32
+ network 11.0.0.40/32
+ network 11.0.0.41/32
+ network 11.0.0.42/32
+ network 11.0.0.43/32
+ network 11.0.0.44/32
+ network 11.0.0.45/32
+ network 11.0.0.46/32
+ network 11.0.0.47/32
+ network 11.0.0.48/32
+ network 11.0.0.49/32
+ network 11.0.0.50/32
+ network 11.0.0.51/32
+ network 11.0.0.52/32
+ network 11.0.0.53/32
+ network 11.0.0.54/32
+ network 11.0.0.55/32
+ network 11.0.0.56/32
+ network 11.0.0.57/32
+ network 11.0.0.58/32
+ network 11.0.0.59/32
+ network 11.0.0.60/32
+ network 11.0.0.61/32
+ network 11.0.0.62/32
+ network 11.0.0.63/32
+ network 11.0.0.64/32
+ network 11.0.0.65/32
+ network 11.0.0.66/32
+ network 11.0.0.67/32
+ network 11.0.0.68/32
+ network 11.0.0.69/32
+ network 11.0.0.70/32
+ network 11.0.0.71/32
+ network 11.0.0.72/32
+ network 11.0.0.73/32
+ network 11.0.0.74/32
+ network 11.0.0.75/32
+ network 11.0.0.76/32
+ network 11.0.0.77/32
+ network 11.0.0.78/32
+ network 11.0.0.79/32
+ network 11.0.0.80/32
+ network 11.0.0.81/32
+ network 11.0.0.82/32
+ network 11.0.0.83/32
+ network 11.0.0.84/32
+ network 11.0.0.85/32
+ network 11.0.0.86/32
+ network 11.0.0.87/32
+ network 11.0.0.88/32
+ network 11.0.0.89/32
+ network 11.0.0.90/32
+ network 11.0.0.91/32
+ network 11.0.0.92/32
+ network 11.0.0.93/32
+ network 11.0.0.94/32
+ network 11.0.0.95/32
+ network 11.0.0.96/32
+ network 11.0.0.97/32
+ network 11.0.0.98/32
+ network 11.0.0.99/32
+ network 11.0.0.100/32
+ network 11.0.0.101/32
+ network 11.0.0.102/32
+ network 11.0.0.103/32
+ network 11.0.0.104/32
+ network 11.0.0.105/32
+ network 11.0.0.106/32
+ network 11.0.0.107/32
+ network 11.0.0.108/32
+ network 11.0.0.109/32
+ network 11.0.0.110/32
+ network 11.0.0.111/32
+ network 11.0.0.112/32
+ network 11.0.0.113/32
+ network 11.0.0.114/32
+ network 11.0.0.115/32
+ network 11.0.0.116/32
+ network 11.0.0.117/32
+ network 11.0.0.118/32
+ network 11.0.0.119/32
+ network 11.0.0.120/32
+ network 11.0.0.121/32
+ network 11.0.0.122/32
+ network 11.0.0.123/32
+ network 11.0.0.124/32
+ network 11.0.0.125/32
+ network 11.0.0.126/32
+ network 11.0.0.127/32
+ network 11.0.0.128/32
+ network 11.0.0.129/32
+ network 11.0.0.130/32
+ network 11.0.0.131/32
+ network 11.0.0.132/32
+ network 11.0.0.133/32
+ network 11.0.0.134/32
+ network 11.0.0.135/32
+ network 11.0.0.136/32
+ network 11.0.0.137/32
+ network 11.0.0.138/32
+ network 11.0.0.139/32
+ network 11.0.0.140/32
+ network 11.0.0.141/32
+ network 11.0.0.142/32
+ network 11.0.0.143/32
+ network 11.0.0.144/32
+ network 11.0.0.145/32
+ network 11.0.0.146/32
+ network 11.0.0.147/32
+ network 11.0.0.148/32
+ network 11.0.0.149/32
+ network 11.0.0.150/32
+ network 11.0.0.151/32
+ network 11.0.0.152/32
+ network 11.0.0.153/32
+ network 11.0.0.154/32
+ network 11.0.0.155/32
+ network 11.0.0.156/32
+ network 11.0.0.157/32
+ network 11.0.0.158/32
+ network 11.0.0.159/32
+ network 11.0.0.160/32
+ network 11.0.0.161/32
+ network 11.0.0.162/32
+ network 11.0.0.163/32
+ network 11.0.0.164/32
+ network 11.0.0.165/32
+ network 11.0.0.166/32
+ network 11.0.0.167/32
+ network 11.0.0.168/32
+ network 11.0.0.169/32
+ network 11.0.0.170/32
+ network 11.0.0.171/32
+ network 11.0.0.172/32
+ network 11.0.0.173/32
+ network 11.0.0.174/32
+ network 11.0.0.175/32
+ network 11.0.0.176/32
+ network 11.0.0.177/32
+ network 11.0.0.178/32
+ network 11.0.0.179/32
+ network 11.0.0.180/32
+ network 11.0.0.181/32
+ network 11.0.0.182/32
+ network 11.0.0.183/32
+ network 11.0.0.184/32
+ network 11.0.0.185/32
+ network 11.0.0.186/32
+ network 11.0.0.187/32
+ network 11.0.0.188/32
+ network 11.0.0.189/32
+ network 11.0.0.190/32
+ network 11.0.0.191/32
+ network 11.0.0.192/32
+ network 11.0.0.193/32
+ network 11.0.0.194/32
+ network 11.0.0.195/32
+ network 11.0.0.196/32
+ network 11.0.0.197/32
+ network 11.0.0.198/32
+ network 11.0.0.199/32
+ network 11.0.0.200/32
+ network 11.0.0.201/32
+ network 11.0.0.202/32
+ network 11.0.0.203/32
+ network 11.0.0.204/32
+ network 11.0.0.205/32
+ network 11.0.0.206/32
+ network 11.0.0.207/32
+ network 11.0.0.208/32
+ network 11.0.0.209/32
+ network 11.0.0.210/32
+ network 11.0.0.211/32
+ network 11.0.0.212/32
+ network 11.0.0.213/32
+ network 11.0.0.214/32
+ network 11.0.0.215/32
+ network 11.0.0.216/32
+ network 11.0.0.217/32
+ network 11.0.0.218/32
+ network 11.0.0.219/32
+ network 11.0.0.220/32
+ network 11.0.0.221/32
+ network 11.0.0.222/32
+ network 11.0.0.223/32
+ network 11.0.0.224/32
+ network 11.0.0.225/32
+ network 11.0.0.226/32
+ network 11.0.0.227/32
+ network 11.0.0.228/32
+ network 11.0.0.229/32
+ network 11.0.0.230/32
+ network 11.0.0.231/32
+ network 11.0.0.232/32
+ network 11.0.0.233/32
+ network 11.0.0.234/32
+ network 11.0.0.235/32
+ network 11.0.0.236/32
+ network 11.0.0.237/32
+ network 11.0.0.238/32
+ network 11.0.0.239/32
+ network 11.0.0.240/32
+ network 11.0.0.241/32
+ network 11.0.0.242/32
+ network 11.0.0.243/32
+ network 11.0.0.244/32
+ network 11.0.0.245/32
+ network 11.0.0.246/32
+ network 11.0.0.247/32
+ network 11.0.0.248/32
+ network 11.0.0.249/32
+ network 11.0.0.250/32
+ network 11.0.0.251/32
+ network 11.0.0.252/32
+ network 11.0.0.253/32
+ network 11.0.1.1/32
+ network 11.0.1.2/32
+ network 11.0.1.3/32
+ network 11.0.1.4/32
+ network 11.0.1.5/32
+ network 11.0.1.6/32
+ network 11.0.1.7/32
+ network 11.0.1.8/32
+ network 11.0.1.9/32
+ network 11.0.1.10/32
+ network 11.0.1.11/32
+ network 11.0.1.12/32
+ network 11.0.1.13/32
+ network 11.0.1.14/32
+ network 11.0.1.15/32
+ network 11.0.1.16/32
+ network 11.0.1.17/32
+ network 11.0.1.18/32
+ network 11.0.1.19/32
+ network 11.0.1.20/32
+ network 11.0.1.21/32
+ network 11.0.1.22/32
+ network 11.0.1.23/32
+ network 11.0.1.24/32
+ network 11.0.1.25/32
+ network 11.0.1.26/32
+ network 11.0.1.27/32
+ network 11.0.1.28/32
+ network 11.0.1.29/32
+ network 11.0.1.30/32
+ network 11.0.1.31/32
+ network 11.0.1.32/32
+ network 11.0.1.33/32
+ network 11.0.1.34/32
+ network 11.0.1.35/32
+ network 11.0.1.36/32
+ network 11.0.1.37/32
+ network 11.0.1.38/32
+ network 11.0.1.39/32
+ network 11.0.1.40/32
+ network 11.0.1.41/32
+ network 11.0.1.42/32
+ network 11.0.1.43/32
+ network 11.0.1.44/32
+ network 11.0.1.45/32
+ network 11.0.1.46/32
+ network 11.0.1.47/32
+ network 11.0.1.48/32
+ network 11.0.1.49/32
+ network 11.0.1.50/32
+ network 11.0.1.51/32
+ network 11.0.1.52/32
+ network 11.0.1.53/32
+ network 11.0.1.54/32
+ network 11.0.1.55/32
+ network 11.0.1.56/32
+ network 11.0.1.57/32
+ network 11.0.1.58/32
+ network 11.0.1.59/32
+ network 11.0.1.60/32
+ network 11.0.1.61/32
+ network 11.0.1.62/32
+ network 11.0.1.63/32
+ network 11.0.1.64/32
+ network 11.0.1.65/32
+ network 11.0.1.66/32
+ network 11.0.1.67/32
+ network 11.0.1.68/32
+ network 11.0.1.69/32
+ network 11.0.1.70/32
+ network 11.0.1.71/32
+ network 11.0.1.72/32
+ network 11.0.1.73/32
+ network 11.0.1.74/32
+ network 11.0.1.75/32
+ network 11.0.1.76/32
+ network 11.0.1.77/32
+ network 11.0.1.78/32
+ network 11.0.1.79/32
+ network 11.0.1.80/32
+ network 11.0.1.81/32
+ network 11.0.1.82/32
+ network 11.0.1.83/32
+ network 11.0.1.84/32
+ network 11.0.1.85/32
+ network 11.0.1.86/32
+ network 11.0.1.87/32
+ network 11.0.1.88/32
+ network 11.0.1.89/32
+ network 11.0.1.90/32
+ network 11.0.1.91/32
+ network 11.0.1.92/32
+ network 11.0.1.93/32
+ network 11.0.1.94/32
+ network 11.0.1.95/32
+ network 11.0.1.96/32
+ network 11.0.1.97/32
+ network 11.0.1.98/32
+ network 11.0.1.99/32
+ network 11.0.1.100/32
+ network 11.0.1.101/32
+ network 11.0.1.102/32
+ network 11.0.1.103/32
+ network 11.0.1.104/32
+ network 11.0.1.105/32
+ network 11.0.1.106/32
+ network 11.0.1.107/32
+ network 11.0.1.108/32
+ network 11.0.1.109/32
+ network 11.0.1.110/32
+ network 11.0.1.111/32
+ network 11.0.1.112/32
+ network 11.0.1.113/32
+ network 11.0.1.114/32
+ network 11.0.1.115/32
+ network 11.0.1.116/32
+ network 11.0.1.117/32
+ network 11.0.1.118/32
+ network 11.0.1.119/32
+ network 11.0.1.120/32
+ network 11.0.1.121/32
+ network 11.0.1.122/32
+ network 11.0.1.123/32
+ network 11.0.1.124/32
+ network 11.0.1.125/32
+ network 11.0.1.126/32
+ network 11.0.1.127/32
+ network 11.0.1.128/32
+ network 11.0.1.129/32
+ network 11.0.1.130/32
+ network 11.0.1.131/32
+ network 11.0.1.132/32
+ network 11.0.1.133/32
+ network 11.0.1.134/32
+ network 11.0.1.135/32
+ network 11.0.1.136/32
+ network 11.0.1.137/32
+ network 11.0.1.138/32
+ network 11.0.1.139/32
+ network 11.0.1.140/32
+ network 11.0.1.141/32
+ network 11.0.1.142/32
+ network 11.0.1.143/32
+ network 11.0.1.144/32
+ network 11.0.1.145/32
+ network 11.0.1.146/32
+ network 11.0.1.147/32
+ network 11.0.1.148/32
+ network 11.0.1.149/32
+ network 11.0.1.150/32
+ network 11.0.1.151/32
+ network 11.0.1.152/32
+ network 11.0.1.153/32
+ network 11.0.1.154/32
+ network 11.0.1.155/32
+ network 11.0.1.156/32
+ network 11.0.1.157/32
+ network 11.0.1.158/32
+ network 11.0.1.159/32
+ network 11.0.1.160/32
+ network 11.0.1.161/32
+ network 11.0.1.162/32
+ network 11.0.1.163/32
+ network 11.0.1.164/32
+ network 11.0.1.165/32
+ network 11.0.1.166/32
+ network 11.0.1.167/32
+ network 11.0.1.168/32
+ network 11.0.1.169/32
+ network 11.0.1.170/32
+ network 11.0.1.171/32
+ network 11.0.1.172/32
+ network 11.0.1.173/32
+ network 11.0.1.174/32
+ network 11.0.1.175/32
+ network 11.0.1.176/32
+ network 11.0.1.177/32
+ network 11.0.1.178/32
+ network 11.0.1.179/32
+ network 11.0.1.180/32
+ network 11.0.1.181/32
+ network 11.0.1.182/32
+ network 11.0.1.183/32
+ network 11.0.1.184/32
+ network 11.0.1.185/32
+ network 11.0.1.186/32
+ network 11.0.1.187/32
+ network 11.0.1.188/32
+ network 11.0.1.189/32
+ network 11.0.1.190/32
+ network 11.0.1.191/32
+ network 11.0.1.192/32
+ network 11.0.1.193/32
+ network 11.0.1.194/32
+ network 11.0.1.195/32
+ network 11.0.1.196/32
+ network 11.0.1.197/32
+ network 11.0.1.198/32
+ network 11.0.1.199/32
+ network 11.0.1.200/32
+ network 11.0.1.201/32
+ network 11.0.1.202/32
+ network 11.0.1.203/32
+ network 11.0.1.204/32
+ network 11.0.1.205/32
+ network 11.0.1.206/32
+ network 11.0.1.207/32
+ network 11.0.1.208/32
+ network 11.0.1.209/32
+ network 11.0.1.210/32
+ network 11.0.1.211/32
+ network 11.0.1.212/32
+ network 11.0.1.213/32
+ network 11.0.1.214/32
+ network 11.0.1.215/32
+ network 11.0.1.216/32
+ network 11.0.1.217/32
+ network 11.0.1.218/32
+ network 11.0.1.219/32
+ network 11.0.1.220/32
+ network 11.0.1.221/32
+ network 11.0.1.222/32
+ network 11.0.1.223/32
+ network 11.0.1.224/32
+ network 11.0.1.225/32
+ network 11.0.1.226/32
+ network 11.0.1.227/32
+ network 11.0.1.228/32
+ network 11.0.1.229/32
+ network 11.0.1.230/32
+ network 11.0.1.231/32
+ network 11.0.1.232/32
+ network 11.0.1.233/32
+ network 11.0.1.234/32
+ network 11.0.1.235/32
+ network 11.0.1.236/32
+ network 11.0.1.237/32
+ network 11.0.1.238/32
+ network 11.0.1.239/32
+ network 11.0.1.240/32
+ network 11.0.1.241/32
+ network 11.0.1.242/32
+ network 11.0.1.243/32
+ network 11.0.1.244/32
+ network 11.0.1.245/32
+ network 11.0.1.246/32
+ network 11.0.1.247/32
+ network 11.0.1.248/32
+ network 11.0.1.249/32
+ network 11.0.1.250/32
+ network 11.0.1.251/32
+ network 11.0.1.252/32
+ network 11.0.1.253/32
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_lu_topo1/R3/zebra.conf b/tests/topotests/bgp_lu_topo1/R3/zebra.conf
new file mode 100644
index 0000000..ea4a148
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/R3/zebra.conf
@@ -0,0 +1,9 @@
+log file /tmp/zebra.log
+!
+! debug zebra events
+! debug zebra packet detail
+! debug zebra mpls
+!
+interface R3-eth0
+ ip address 10.0.1.3/24
+!
diff --git a/tests/topotests/bgp_lu_topo1/test_bgp_lu.py b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
new file mode 100644
index 0000000..8955f27
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo1/test_bgp_lu.py
@@ -0,0 +1,181 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_lu.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_lu.py: Test BGP LU label allocation
+"""
+
+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
+
+# Required to instantiate the topology builder class.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Basic scenario for BGP-LU. Nodes are directly connected.
+# Node 3 is advertising many routes to 2, which advertises them
+# as BGP-LU to 1; this way we get routes with actual labels, as
+# opposed to implicit-null routes in the 2-node case.
+#
+# AS1 BGP-LU AS2 iBGP AS2
+# +-----+ +-----+ +-----+
+# | |.1 .2| |.2 .3| |
+# | 1 +----------------+ 2 +-----------------+ 3 |
+# | | 10.0.0.0/24 | | 10.0.1.0/24 | |
+# +-----+ +-----+ +-----+
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+
+ # R1-R2
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["R1"])
+ switch.add_link(tgen.gears["R2"])
+
+ # R2-R3
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["R2"])
+ switch.add_link(tgen.gears["R3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 check_labelpool(router):
+ json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp labelpool summary json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_converge_bgplu():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # tgen.mininet_cli();
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+
+def test_clear_bgplu():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # tgen.mininet_cli();
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+
+ r1.vtysh_cmd("clear bgp 10.0.0.2")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+ r2.vtysh_cmd("clear bgp 10.0.1.3")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+ r1.vtysh_cmd("clear bgp 10.0.0.2")
+ r2.vtysh_cmd("clear bgp 10.0.1.3")
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+
+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_lu_topo2/R1/bgpd.conf b/tests/topotests/bgp_lu_topo2/R1/bgpd.conf
new file mode 100644
index 0000000..9fe4026
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R1/bgpd.conf
@@ -0,0 +1,29 @@
+!
+no log unique-id
+!
+! debug bgp labelpool
+! debug bgp zebra
+!
+router bgp 1
+ bgp router-id 10.0.0.1
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.0.2 remote-as 2
+! neighbor 10.0.0.2 solo
+ neighbor 10.0.0.2 timers connect 10
+ neighbor 10.0.4.4 remote-as 4
+! neighbor 10.0.4.4 solo
+ neighbor 10.0.4.4 timers connect 10
+!
+ address-family ipv4 unicast
+ no neighbor 10.0.0.2 activate
+ no neighbor 10.0.4.4 activate
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.2 activate
+ neighbor 10.0.4.4 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json
new file mode 100644
index 0000000..0dc59b5
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R1/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":51,
+ "InUse":51,
+ "Requests":0,
+ "LabelChunks":1,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo2/R1/zebra.conf b/tests/topotests/bgp_lu_topo2/R1/zebra.conf
new file mode 100644
index 0000000..64c34a3
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R1/zebra.conf
@@ -0,0 +1,13 @@
+!
+no log unique-id
+!
+! debug zebra events
+! debug zebra rib det
+! debug zebra dplane
+! debug zebra mpls
+!
+interface R1-eth0
+ ip address 10.0.0.1/24
+!
+interface R1-eth1
+ ip address 10.0.4.1/24
diff --git a/tests/topotests/bgp_lu_topo2/R2/bgpd.conf b/tests/topotests/bgp_lu_topo2/R2/bgpd.conf
new file mode 100644
index 0000000..917060c
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R2/bgpd.conf
@@ -0,0 +1,27 @@
+!
+no log unique-id
+!
+! debug bgp labelpool
+! debug bgp zebra
+!
+router bgp 2
+ bgp router-id 10.0.0.2
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 3 9
+ neighbor 10.0.0.1 remote-as 1
+ neighbor 10.0.0.1 timers connect 10
+ neighbor 10.0.1.3 remote-as 2
+ neighbor 10.0.1.3 update-source 10.0.1.2
+ neighbor 10.0.1.3 timers connect 10
+!
+ address-family ipv4 unicast
+ network 10.0.0.0/24
+ neighbor 10.0.1.3 activate
+ no neighbor 10.0.0.1 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json
new file mode 100644
index 0000000..eb1ae93
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R2/labelpool.summ.json
@@ -0,0 +1,8 @@
+{
+ "Ledger":1,
+ "InUse":1,
+ "Requests":0,
+ "LabelChunks":1,
+ "Pending":0,
+ "Reconnects":0
+}
diff --git a/tests/topotests/bgp_lu_topo2/R2/zebra.conf b/tests/topotests/bgp_lu_topo2/R2/zebra.conf
new file mode 100644
index 0000000..f465914
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R2/zebra.conf
@@ -0,0 +1,14 @@
+!
+no log unique-id
+!
+! debug zebra events
+! debug zebra dplane
+! debug zebra mpls
+! debug zebra rib det
+!
+interface R2-eth0
+ ip address 10.0.0.2/24
+!
+interface R2-eth1
+ ip address 10.0.1.2/24
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_lu_topo2/R3/bgpd.conf b/tests/topotests/bgp_lu_topo2/R3/bgpd.conf
new file mode 100644
index 0000000..6443445
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R3/bgpd.conf
@@ -0,0 +1,70 @@
+log file /tmp/bgpd.log
+no log unique-id
+!
+!
+! debug bgp updates
+!
+router bgp 2
+ bgp router-id 10.0.1.3
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ timers bgp 3 9
+ neighbor 10.0.1.2 remote-as 2
+ neighbor 10.0.1.2 timers connect 10
+ !
+ address-family ipv4 unicast
+ neighbor 10.0.1.2 activate
+ network 10.0.1.0/24
+ network 11.0.0.1/32
+ network 11.0.0.2/32
+ network 11.0.0.3/32
+ network 11.0.0.4/32
+ network 11.0.0.5/32
+ network 11.0.0.6/32
+ network 11.0.0.7/32
+ network 11.0.0.8/32
+ network 11.0.0.9/32
+ network 11.0.0.10/32
+ network 11.0.0.11/32
+ network 11.0.0.12/32
+ network 11.0.0.13/32
+ network 11.0.0.14/32
+ network 11.0.0.15/32
+ network 11.0.0.16/32
+ network 11.0.0.17/32
+ network 11.0.0.18/32
+ network 11.0.0.19/32
+ network 11.0.0.20/32
+ network 11.0.0.21/32
+ network 11.0.0.22/32
+ network 11.0.0.23/32
+ network 11.0.0.24/32
+ network 11.0.0.25/32
+ network 11.0.0.26/32
+ network 11.0.0.27/32
+ network 11.0.0.28/32
+ network 11.0.0.29/32
+ network 11.0.0.30/32
+ network 11.0.0.31/32
+ network 11.0.0.32/32
+ network 11.0.0.33/32
+ network 11.0.0.34/32
+ network 11.0.0.35/32
+ network 11.0.0.36/32
+ network 11.0.0.37/32
+ network 11.0.0.38/32
+ network 11.0.0.39/32
+ network 11.0.0.40/32
+ network 11.0.0.41/32
+ network 11.0.0.42/32
+ network 11.0.0.43/32
+ network 11.0.0.44/32
+ network 11.0.0.45/32
+ network 11.0.0.46/32
+ network 11.0.0.47/32
+ network 11.0.0.48/32
+ network 11.0.0.49/32
+ network 11.0.0.50/32
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_lu_topo2/R3/staticd.conf b/tests/topotests/bgp_lu_topo2/R3/staticd.conf
new file mode 100644
index 0000000..867fc5a
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R3/staticd.conf
@@ -0,0 +1,5 @@
+log file /tmp/staticd.log
+no log unique-id
+!
+!
+ip route 10.0.4.0/24 10.0.1.2
diff --git a/tests/topotests/bgp_lu_topo2/R3/zebra.conf b/tests/topotests/bgp_lu_topo2/R3/zebra.conf
new file mode 100644
index 0000000..dd24deb
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R3/zebra.conf
@@ -0,0 +1,11 @@
+log file /tmp/zebra.log
+no log unique-id
+!
+!
+! debug zebra events
+! debug zebra packet detail
+! debug zebra mpls
+!
+interface R3-eth0
+ ip address 10.0.1.3/24
+!
diff --git a/tests/topotests/bgp_lu_topo2/R4/bgpd.conf b/tests/topotests/bgp_lu_topo2/R4/bgpd.conf
new file mode 100644
index 0000000..45c81fb
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R4/bgpd.conf
@@ -0,0 +1,23 @@
+!
+no log unique-id
+!
+! debug bgp labelpool
+! debug bgp zebra
+!
+router bgp 4
+ bgp router-id 10.0.4.4
+ timers bgp 3 9
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 10.0.4.1 remote-as 1
+ neighbor 10.0.4.1 solo
+ neighbor 10.0.4.1 timers connect 10
+!
+ address-family ipv4 unicast
+ no neighbor 10.0.4.1 activate
+ exit-address-family
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.4.1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_lu_topo2/R4/zebra.conf b/tests/topotests/bgp_lu_topo2/R4/zebra.conf
new file mode 100644
index 0000000..53ffe51
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/R4/zebra.conf
@@ -0,0 +1,9 @@
+no log unique-id
+!
+! debug zebra events
+! debug zebra dplane
+! debug zebra mpls
+! debug zebra rib det
+!
+interface R4-eth0
+ ip address 10.0.4.4/24
diff --git a/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py b/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py
new file mode 100644
index 0000000..fc9d2c4
--- /dev/null
+++ b/tests/topotests/bgp_lu_topo2/test_bgp_lu2.py
@@ -0,0 +1,221 @@
+#!/usr/bin/env python
+#
+# test_bgp_lu2.py
+#
+# Part of FRR/NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+# Copyright (c) 2021 by Nvidia, Inc.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_lu2.py: Test BGP LU label allocation
+"""
+
+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.
+
+pytestmark = [pytest.mark.bgpd]
+
+#
+# Basic scenario for BGP-LU. Nodes are directly connected.
+# Node 3 is advertising routes to 2, which advertises them
+# as BGP-LU to 1; this way we get routes with actual labels, as
+# opposed to implicit-null routes in the 2-node case.
+#
+# R2 is an LER, with MPLS towards R1, and IP towards R3. R1 is an LSR, with
+# MPLS on both sides.
+#
+#
+# AS4 BGP-LU AS1 BGP-LU AS2 iBGP AS2
+# +-----+ +-----+ +-----+ +-----+
+# | |.4 .1| |.1 .2| |.2 .3| |
+# | 4 +-------------+ 1 +----------------+ 2 +-----------------+ 3 |
+# | | 10.0.4.0/24 | | 10.0.0.0/24 | | 10.0.1.0/24 | |
+# +-----+ +-----+ +-----+ +-----+
+#
+#
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function's only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("R1")
+ tgen.add_router("R2")
+ tgen.add_router("R3")
+ tgen.add_router("R4")
+
+ # R1-R2
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["R1"])
+ switch.add_link(tgen.gears["R2"])
+
+ # R2-R3
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["R2"])
+ switch.add_link(tgen.gears["R3"])
+
+ # R1-R4
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["R1"])
+ switch.add_link(tgen.gears["R4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+
+ # Skip if no mpls support
+ if not tgen.hasmpls:
+ logger.info("MPLS is not available, skipping test")
+ pytest.skip("MPLS is not available, skipping")
+ return
+
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # Enable mpls input for routers, so we can ping
+ sval = "net.mpls.conf.{}.input"
+ topotest.sysctl_assure(router_list["R2"], sval.format("R2-eth0"), 1)
+ topotest.sysctl_assure(router_list["R1"], sval.format("R1-eth0"), 1)
+ topotest.sysctl_assure(router_list["R1"], sval.format("R1-eth1"), 1)
+ topotest.sysctl_assure(router_list["R4"], sval.format("R4-eth0"), 1)
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Have static config for R3 too
+ if router == router_list["R3"]:
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 check_labelpool(router):
+ json_file = "{}/{}/labelpool.summ.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show bgp labelpool summary json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = '"{}" JSON output mismatches - Did not converge'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_converge_bgplu():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # TODO -- enable for debugging
+ # tgen.mininet_cli()
+
+ r1 = tgen.gears["R1"]
+ r2 = tgen.gears["R2"]
+
+ check_labelpool(r1)
+ check_labelpool(r2)
+
+
+def test_ping():
+ "Simple ping tests"
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ logger.info("Ping from R2 to R3")
+ router = tgen.gears["R2"]
+ output = router.run("ping -c 4 -w 4 {}".format("10.0.1.3"))
+ assert " 0% packet loss" in output, "Ping R2->R3 FAILED"
+ logger.info("Ping from R2 to R3 ... success")
+
+ #
+ logger.info("Ping from R4 to R2")
+ router = tgen.gears["R4"]
+ output = router.run("ping -c 4 -w 4 {}".format("10.0.0.2"))
+ assert " 0% packet loss" in output, "Ping R4->R2 FAILED"
+ logger.info("Ping from R4 to R2 ... success")
+
+ #
+ logger.info("Ping from R4 to R3")
+ router = tgen.gears["R4"]
+ output = router.run("ping -c 4 -w 4 {}".format("10.0.1.3"))
+ assert " 0% packet loss" in output, "Ping R4->R3 FAILED"
+ logger.info("Ping from R4 to R3 ... success")
+
+
+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_maximum_prefix_invalid_update/__init__.py b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/__init__.py
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf
new file mode 100644
index 0000000..6211042
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf
new file mode 100644
index 0000000..0a283c0
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r1/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf
new file mode 100644
index 0000000..005425e
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4
+ neighbor 192.168.255.1 maximum-prefix 1
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
new file mode 100644
index 0000000..5c34ebf
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_invalid_update/test_bgp_maximum_prefix_invalid_update.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# bgp_local_as_private_remove.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+bgp_maximum_prefix_invalid_update.py:
+Test if unnecesarry UPDATE message like below:
+
+[Error] Error parsing NLRI
+%NOTIFICATION: sent to neighbor X.X.X.X 3/10 (UPDATE Message Error/Invalid Network Field) 0 bytes
+
+is not sent if maximum-prefix count is overflow.
+"""
+
+import os
+import sys
+import json
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_invalid():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ while True:
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp neighbor 192.168.255.1 json")
+ )
+ if output["192.168.255.1"]["connectionsEstablished"] > 0:
+ return True
+
+ def _bgp_parsing_nlri(router):
+ cmd_max_exceeded = (
+ 'grep "%MAXPFXEXCEED: No. of IPv4 Unicast prefix received" bgpd.log'
+ )
+ cmdt_error_parsing_nlri = 'grep "Error parsing NLRI" bgpd.log'
+ output_max_exceeded = tgen.gears[router].run(cmd_max_exceeded)
+ output_error_parsing_nlri = tgen.gears[router].run(cmdt_error_parsing_nlri)
+
+ if len(output_max_exceeded) > 0:
+ if len(output_error_parsing_nlri) > 0:
+ return False
+ return True
+
+ if _bgp_converge("r2"):
+ assert _bgp_parsing_nlri("r2") == True
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_maximum_prefix_out/__init__.py b/tests/topotests/bgp_maximum_prefix_out/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/__init__.py
diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf
new file mode 100644
index 0000000..adb594b
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/r1/bgpd.conf
@@ -0,0 +1,11 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.255.1 maximum-prefix-out 2
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf
new file mode 100644
index 0000000..2416225
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/r1/zebra.conf
@@ -0,0 +1,13 @@
+!
+interface lo
+ ip address 172.16.255.250/32
+ ip address 172.16.255.251/32
+ ip address 172.16.255.252/32
+ ip address 172.16.255.253/32
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf
new file mode 100644
index 0000000..229de2e
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/r2/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf
new file mode 100644
index 0000000..08dd374
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
new file mode 100644
index 0000000..696d6d9
--- /dev/null
+++ b/tests/topotests/bgp_maximum_prefix_out/test_bgp_maximum_prefix_out.py
@@ -0,0 +1,200 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_maximum_prefix_out.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if `neighbor <X.X.X.X> maximum-prefix-out <Y>` is working
+correctly.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_out():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ # format (router to configure, command, expected received prefixes on r2)
+ tests = [
+ # test of the initial config
+ (None, 2),
+ # modifying the max-prefix-out value
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4",
+ 4,
+ ),
+ # removing the max-prefix-out value
+ (
+ "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out",
+ 6,
+ ),
+ # setting a max-prefix-out value
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 3",
+ 3,
+ ),
+ # setting a max-prefix-out value - higher than the total number of prefix
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 8",
+ 6,
+ ),
+ # adding a new prefix
+ ("router bgp\n int lo\n ip address 172.16.255.249/32", 7),
+ # setting a max-prefix-out value - lower than the total number of prefix
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 1",
+ 1,
+ ),
+ # adding a new prefix
+ ("router bgp\n int lo\n ip address 172.16.255.248/32", 1),
+ # removing the max-prefix-out value
+ (
+ "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 1",
+ 8,
+ ),
+ # test setting the existing neighbor into a peer-group with a max-prefix-out value
+ (
+ """
+ router bgp
+ neighbor test peer-group
+ neighbor test remote-as 65002
+ neighbor test timers 3 10
+ address-family ipv4
+ neighbor test maximum-prefix-out 3
+ !
+ neighbor 192.168.255.1 peer-group test
+ """,
+ 3,
+ ),
+ # max-prefix-out value of the neighbor must take the precedence
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 4",
+ 4,
+ ),
+ (
+ "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out",
+ 3,
+ ),
+ (
+ """
+ router bgp
+ no neighbor 192.168.255.1 peer-group test
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ """,
+ 8,
+ ),
+ (
+ "router bgp\n address-family ipv4\n neighbor 192.168.255.1 maximum-prefix-out 5",
+ 5,
+ ),
+ # test setting the existing neighbor with a max-pref-out value into a peer-group with a max-pref-out value
+ ("router bgp\n neighbor 192.168.255.1 peer-group test", 5),
+ (
+ "router bgp\n address-family ipv4\n no neighbor 192.168.255.1 maximum-prefix-out 5",
+ 3,
+ ),
+ ]
+
+ def _bgp_converge(router, nb_prefixes):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {
+ "ipv4Unicast": {"acceptedPrefixCounter": nb_prefixes}
+ },
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ for test in tests:
+ cfg, exp_prfxs = test
+ if cfg:
+ cmd = (
+ """
+ configure terminal
+ %s
+ """
+ % cfg
+ )
+ router1.vtysh_cmd(cmd)
+
+ test_func = functools.partial(_bgp_converge, router2, exp_prfxs)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(router2)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_minimum_holdtime/__init__.py b/tests/topotests/bgp_minimum_holdtime/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/__init__.py
diff --git a/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf
new file mode 100644
index 0000000..847a2d4
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/r1/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ bgp minimum-holdtime 20
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 timers connect 10
+!
diff --git a/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf
new file mode 100644
index 0000000..e2c399e
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf
new file mode 100644
index 0000000..6d1080c
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py
new file mode 100755
index 0000000..b1641b3
--- /dev/null
+++ b/tests/topotests/bgp_minimum_holdtime/test_bgp_minimum_holdtime.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Takemasa Imada <takemasa.imada@gmail.com>
+#
+# 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 if minimum-holdtime works.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_minimum_holdtime():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_neighbor_check_if_notification_sent():
+ output = json.loads(
+ tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor 192.168.255.2 json")
+ )
+ expected = {
+ "192.168.255.2": {
+ "connectionsEstablished": 0,
+ "lastNotificationReason": "OPEN Message Error/Unacceptable Hold Time",
+ "lastResetDueTo": "BGP Notification send",
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_neighbor_check_if_notification_sent)
+ success, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+ assert result is None, "Failed to send notification message\n"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json
new file mode 100644
index 0000000..327744d
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo1/bgp_multi_vrf_topo1.json
@@ -0,0 +1,884 @@
+{
+ "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": {
+ "red1": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "blue1": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE_A",
+ "id": "1"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r1": {
+ "links": {
+ "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link1":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link1":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link2":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link2":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link3":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link3":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link4":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link4":
+ { "next_hop_self": true }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"},
+ "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {}
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {}
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {}
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {}
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "red2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "blue2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE_A",
+ "id": "1"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
new file mode 100644
index 0000000..7eb5bda
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo1/test_bgp_multi_vrf_topo1.py
@@ -0,0 +1,6290 @@
+#!/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 BGP Multi-VRF:
+
+FUNC_1:
+ Within each VRF, each address must be unambiguous on DUT.
+FUNC_2:
+ Different VRFs can have ambiguous/overlapping
+ addresses on DUT.
+FUNC_3:
+ Create static routes(IPv4+IPv6) associated to specific VRFs
+ and verify on DUT that same prefixes are present in corresponding
+ routing table.
+FUNC_4_&_5:
+ Each VRF should be mapped with a unique VLAN on DUT
+ for traffic segregation, when using a single physical interface.
+FUNC_6:
+ Advertise same set of prefixes from different VRFs
+ and verify on remote router that these prefixes are not
+ leaking to each other
+FUNC_7:
+ Redistribute Static routes and verify on remote routers
+ that routes are advertised within specific VRF instance, which
+ those static routes belong to.
+FUNC_8:
+ Test end to end traffic isolation based on VRF tables.
+FUNC_9:
+ Use static routes for inter-vrf communication
+ (route-leaking) on DUT.
+FUNC_10:
+ Verify intra-vrf and inter-vrf communication between
+ iBGP peers.
+FUNC_11:
+ Verify intra-vrf and inter-vrf communication
+ between eBGP peers.
+FUNC_12_a:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_12_b:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_12_c:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_12_d:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_12_e:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_12_f:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+FUNC_13:
+ Configure a route-map on DUT to match traffic based
+ on a VRF interfaces.
+FUNC_14:
+ Test VRF-lite with Static+BGP originated routes.
+FUNC_15:
+ Configure prefix-lists on DUT and apply to BGP peers to
+ permit/deny prefixes.
+FUNC_16_1:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+FUNC_16_2:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+FUNC_16_3:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+"""
+
+import os
+import sys
+import time
+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, "../"))
+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.topogen import Topogen, get_topogen
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import (
+ step,
+ verify_rib,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_route_maps,
+ create_static_routes,
+ create_prefix_lists,
+ create_interface_in_kernel,
+ create_bgp_community_lists,
+ check_router_status,
+ apply_raw_config,
+ required_linux_kernel_version,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_rib,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_convergence,
+ verify_best_path_as_per_bgp_attribute,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"}
+NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"}
+NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"}
+NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"}
+NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"}
+NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"}
+NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"}
+NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"}
+NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"}
+NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"}
+NETWORK6_1 = {"ipv4": "6.1.1.1/32", "ipv6": "6::1/128"}
+NETWORK6_2 = {"ipv4": "6.1.1.2/32", "ipv6": "6::2/128"}
+NETWORK7_1 = {"ipv4": "7.1.1.1/32", "ipv6": "7::1/128"}
+NETWORK7_2 = {"ipv4": "7.1.1.2/32", "ipv6": "7::2/128"}
+NETWORK8_1 = {"ipv4": "8.1.1.1/32", "ipv6": "8::1/128"}
+NETWORK8_2 = {"ipv4": "8.1.1.2/32", "ipv6": "8::2/128"}
+
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+LOOPBACK_1 = {
+ "ipv4": "10.10.10.10/32",
+ "ipv6": "10::10:10/128",
+}
+LOOPBACK_2 = {
+ "ipv4": "20.20.20.20/32",
+ "ipv6": "20::20:20/128",
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ 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...
+ json_file = "{}/bgp_multi_vrf_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_address_unambiguous_within_each_vrf_p0(request):
+ """
+ FUNC_1:
+ Within each VRF, each address must be unambiguous on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure a set of static routes(IPv4+IPv6) in " "RED_A on router RED-1")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure the same static routes(IPv4+IPv6) with a TAG value"
+ "of 500 in RED_A on router RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 500,
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {
+ "red1": {
+ "bgp": {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes(IPv4+IPv6) is overridden and doesn't"
+ " have duplicate entries within VRF RED_A on router RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 500,
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Make sure routes are not present in global routing table")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected Behaviour: Routes are not "
+ "present on Global Routing table \n Error {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ambiguous_overlapping_addresses_in_different_vrfs_p0(request):
+ """
+ FUNC_2:
+ Different VRFs can have ambiguous/overlapping
+ addresses on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure a set of static routes(IPv4+IPv6) in vrf RED_A" "on router RED-1")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure the same static routes(IPv4+IPv6) with a"
+ " TAG value of 500 in vrf RED_B on router RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 500,
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that RED_A has the static routes without any" " TAG value")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, tag=500, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are present with tag value 500 \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Verify that RED_B has the same routes with TAG value "
+ "500 on same device RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 500,
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, tag=500)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Make sure routes are not present in global routing table")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected Behaviour: Routes are not "
+ "present on Global Routing table \n Error {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_routes_associated_to_specific_vrfs_p0(request):
+ """
+ FUNC_3:
+ Create static routes(IPv4+IPv6) associated to specific VRFs
+ and verify on DUT that same prefixes are present in corresponding
+ routing table.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Configure a set of unique static(IPv4+IPv6) routes in vrf"
+ " RED_A on router RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure set of unique static routes(IPv4+IPv6) in vrf "
+ "RED_B on router RED-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes 1.x.x.x/32 and 1::x/128 appear " "in VRF RED_A table"
+ )
+ step(
+ "Verify that static routes 2.x.x.x/32 and 2::x/128 appear " "in VRF RED_B table"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that static routes 1.x.x.x/32 and 1::x/128 appear "
+ "in VRF BLUE_A table"
+ )
+ step(
+ "Verify that static routes 2.x.x.x/32 and 2::x/128 appear "
+ "in VRF BLUE_B table"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue1"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Make sure routes are not present in global routing table")
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue1"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected Behaviour: Routes are not "
+ "present on Global Routing table \n Error {}".format(tc_name, result)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_vrf_with_unique_physical_interface_p0(request):
+ """
+ FUNC_4_&_5:
+ Each VRF should be mapped with a unique VLAN on DUT
+ for traffic segregation, when using a single physical interface.
+
+ Each VRF should be mapped to a unique physical
+ interface(without VLAN tagging) on DUT for traffic segregation.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "R1 is receiving routes in 4 VRFs instances "
+ "(RED_A, RED_B, BLUE_A, BLUE_B) from RED_1 and BLUE_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise a set of unique BGP prefixes(IPv4+IPv6) from "
+ "routers RED_1 & BLUE_1 in each VRF using static redistribution"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Each VRF table on R2 should maintain it's associated "
+ "routes and and accordingly install in zebra"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_prefixes_leaking_p0(request):
+ """
+ FUNC_6:
+ Advertise same set of prefixes from different VRFs
+ and verify on remote router that these prefixes are not
+ leaking to each other
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configure a set of static routes(IPv4+IPv6) in vrf " "RED_A on router RED-1")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ },
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a set of static routes(IPv4+IPv6) in vrf " "BLUE_A on router BLUE-1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ },
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ }
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure the same set of static routes with a "
+ "metric value of 123 in vrf RED_B on router RED-1"
+ )
+ step(
+ "Configure the same set of static routes with a "
+ "metric value of 123 in vrf BLUE_B on router BLUE-1"
+ )
+
+ input_dict_3 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue1": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R1 that RED_A doesn't receive any static "
+ "route with metric value 123"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ },
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ },
+ }
+
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ },
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ }
+ ]
+ },
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1, metric=123, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are present with metric value 123 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, metric=123)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_2, metric=0, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are present with metric value 0 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_static_routes_advertised_within_specific_vrf_p0(request):
+ """
+ FUNC_7:
+ Redistribute Static routes and verify on remote routers
+ that routes are advertised within specific VRF instance, which
+ those static routes belong to.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise a set of unique BGP prefixes(IPv4+IPv6) "
+ "through static redistribution into VRF RED_A and RED_B"
+ " from router RED-1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same as above set of BGP prefixes(IPv4+IPv6) "
+ "through static redistribution into VRF BLUE_A and BLUE_B"
+ " from router BLUE-1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes are installed into vrfs RED_A"
+ "and RED_B tables only, not in global routing table of RED_1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that static routes are installed into vrfs BLUE_A and"
+ "BLUE_B tables only, not in global routing table of BLUE_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue1"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on router R1, that each set of prefixes is received"
+ " into associated vrf tables only."
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_end_to_end_traffic_isolation_p0(request):
+ """
+ FUNC_8:
+ Test end to end traffic isolation based on VRF tables.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 "
+ "in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Use below commands to send prefixes with as-path prepend"
+ "VRF BLUE_A and BLUE_B from router BLUE-1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "blue1": {
+ "route_maps": {
+ "ASP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"path": {"as_num": 123, "as_action": "prepend"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply route-map to neighbours")
+
+ input_dict_5 = {
+ "blue1": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R1 that BLUE_A and BLUE_B VRFs are receiving the"
+ " prefixes with as-path 123 prepended."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_6 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_7 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Use below commands to send prefixes with as-path prepend VRF"
+ " BLUE_A and BLUE_B from router BLUE-1."
+ )
+
+ input_dict_6 = {
+ "red2": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue2": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that router RED-2 receives the prefixes in respective" " VRF tables.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red2"
+ input_dict_6 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue2"
+ input_dict_7 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_routes_for_inter_vrf_route_leaking_p0(request):
+ """
+ FUNC_9:
+ Use static routes for inter-vrf communication
+ (route-leaking) on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Configure unique loopback interfaces in VRFs RED_A "
+ "and RED_B on router RED_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ create_interface_in_kernel(
+ tgen,
+ "red1",
+ "loopback1",
+ LOOPBACK_1[addr_type],
+ "RED_A",
+ )
+ create_interface_in_kernel(
+ tgen,
+ "red1",
+ "loopback2",
+ LOOPBACK_2[addr_type],
+ "RED_B",
+ )
+
+ step(
+ "Create a static routes in vrf RED_B on router RED_1 pointing"
+ " next-hop as interface's IP in vrf RED_A"
+ )
+
+ intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link1"]["interface"]
+ intf_red1_r10 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"]
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_red1_r10,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_A",
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes are installed into vrfs RED_A"
+ "and RED_B tables only, not in global routing table of RED_1"
+ )
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_red1_r10,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_A",
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_inter_vrf_and_intra_vrf_communication_iBGP_p0(request):
+ """
+ FUNC_10:
+ Verify intra-vrf and inter-vrf communication between
+ iBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router"
+ " R1 and advertise it in BGP process using redistribute "
+ "connected command."
+ )
+
+ for addr_type in ADDR_TYPES:
+ create_interface_in_kernel(
+ tgen,
+ "r1",
+ "loopback1",
+ LOOPBACK_1[addr_type],
+ "RED_A",
+ )
+
+ create_interface_in_kernel(
+ tgen,
+ "r1",
+ "loopback2",
+ LOOPBACK_2[addr_type],
+ "BLUE_A",
+ )
+
+ step(
+ "Create a static routes in vrf RED_B on router RED_1 pointing"
+ " next-hop as interface's IP in vrf RED_A"
+ )
+
+ intf_r2_r12 = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ intf_r2_r10 = topo["routers"]["r2"]["links"]["r1-link3"]["interface"]
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_r2_r10,
+ "nexthop_vrf": "BLUE_A",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_r2_r12,
+ "nexthop_vrf": "RED_A",
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute connected..")
+
+ input_dict_3 = {}
+ for dut in ["r1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ VRFS = ["RED_A", "BLUE_A"]
+ AS_NUM = [100, 100]
+
+ 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": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["r2"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ VRFS = ["RED_A", "BLUE_A"]
+ AS_NUM = [100, 100]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes are installed into vrfs RED_A"
+ "and RED_B tables only, not in global routing table of RED_1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_r2_r10,
+ "nexthop_vrf": "BLUE_A",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_r2_r12,
+ "nexthop_vrf": "RED_A",
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_inter_vrf_and_intra_vrf_communication_eBGP_p0(request):
+ """
+ FUNC_11:
+ Verify intra-vrf and inter-vrf communication
+ between eBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Configure unique loopback IP(IPv4+IPv6) in vrf RED_A on router"
+ " R2 and advertise it in BGP process using redistribute "
+ "connected command."
+ )
+
+ step(
+ "Configure unique loopback IP(IPv4+IPv6) in vrf BLUE_A on router"
+ " R2 and advertise it in BGP process using redistribute "
+ "connected command."
+ )
+
+ for addr_type in ADDR_TYPES:
+ create_interface_in_kernel(
+ tgen,
+ "r2",
+ "loopback1",
+ LOOPBACK_1[addr_type],
+ "RED_A",
+ )
+ create_interface_in_kernel(
+ tgen,
+ "r2",
+ "loopback2",
+ LOOPBACK_2[addr_type],
+ "BLUE_A",
+ )
+
+ step(
+ "Create a static routes in vrf RED_B on router RED_1 pointing"
+ " next-hop as interface's IP in vrf RED_A"
+ )
+
+ intf_r3_r21 = topo["routers"]["r3"]["links"]["r2-link1"]["interface"]
+ intf_r3_r23 = topo["routers"]["r3"]["links"]["r2-link3"]["interface"]
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_r3_r23,
+ "nexthop_vrf": "BLUE_A",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_r3_r21,
+ "nexthop_vrf": "RED_A",
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["r3"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ VRFS = ["RED_A", "BLUE_A"]
+ AS_NUM = [200, 200]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute connected..")
+
+ input_dict_3 = {}
+ for dut in ["r2"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ VRFS = ["RED_A", "BLUE_A"]
+ AS_NUM = [100, 100]
+
+ 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": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes are installed into vrfs RED_A"
+ "and RED_B tables only, not in global routing table of RED_1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_r3_r23,
+ "nexthop_vrf": "BLUE_A",
+ "vrf": "RED_A",
+ },
+ {
+ "network": LOOPBACK_1[addr_type],
+ "interface": intf_r3_r21,
+ "nexthop_vrf": "RED_A",
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_within_vrf_to_alter_bgp_attribute_nexthop_p0(request):
+ """
+ FUNC_12_a:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and"
+ " RED_2 in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Delete nexthop-self configure from r1")
+
+ input_dict_4 = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {"next_hop_self": False}
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {"next_hop_self": False}
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because nexthop-self config is deleted \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected Behaviour: Routes are rejected because nexthop-self config is deleted \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize("attribute", ["locPrf", "weight", "metric"])
+def test_route_map_within_vrf_to_alter_bgp_attribute_p0(request, attribute):
+ """
+ FUNC_12_b/c/d:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and"
+ " RED_2 in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ "red2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ "blue2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "red2", "blue1", "blue2"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure a route-maps to influence BGP parameters - " " Local Preference")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "route_maps": {
+ "rmap_r1_{}".format(addr_type): [
+ {"action": "permit", "set": {attribute: 120}}
+ ],
+ "rmap_r3_{}".format(addr_type): [
+ {"action": "permit", "set": {attribute: 150}}
+ ],
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map")
+ input_dict_4 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r3_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict_1, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict_2, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_within_vrf_to_alter_bgp_attribute_aspath_p0(request):
+ """
+ FUNC_12_e:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and"
+ " RED_2 in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ "red2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ "blue2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "red2", "blue1", "blue2"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure a route-maps to influence BGP parameters - " " Local Preference")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "route_maps": {
+ "rmap_r1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": "111 222", "as_action": "prepend"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map")
+ input_dict_4 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link1": {}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link1": {}}},
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link2": {}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link2": {}}},
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link3": {}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link3": {}}},
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link4": {}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r3": {"dest_link": {"r2-link4": {}}},
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ dut = "r2"
+ attribute = "path"
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict_1, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict_2, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_within_vrf_to_alter_bgp_attribute_lcomm_p0(request):
+ """
+ FUNC_12_f:
+ Configure route-maps within a VRF, to alter BGP attributes.
+ Verify that route-map doesn't affect any other VRF instances'
+ routing on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise a set of BGP prefixes(IPv4+IPv6) from RED_1 and"
+ " RED_2 in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ "red2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ "blue2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ },
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "red2", "blue1", "blue2"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure a route-maps to influence BGP parameters - " " Large-community")
+
+ step("Create standard large commumity-list in r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r2": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "rmap_lcomm_{}".format(addr_type),
+ "value": "1:1:1 1:2:3 2:1:1 2:2:2",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-maps in red1 and r1")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "red1": {
+ "route_maps": {
+ "rmap_red1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"}
+ },
+ }
+ ]
+ }
+ },
+ "r2": {
+ "route_maps": {
+ "rmap_r1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ "large_community_list": {
+ "id": "rmap_lcomm_" + addr_type
+ }
+ },
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map in red1")
+
+ input_dict_4 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure neighbor for route map in r2")
+
+ input_dict_4 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "route_maps": [
+ {
+ "name": "rmap_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "All the prefixes advertised from RED_1 and BLUE_1 should carry"
+ " attributes set by outbound route-maps within specific vrfs. "
+ "Router R1 should be able to match and permit/deny those "
+ "prefixes based on received attributes. Please use below "
+ "commands to verify."
+ )
+
+ input_dict = {
+ "largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2",
+ }
+
+ for addr_type in ADDR_TYPES:
+ vrf = "RED_A"
+ routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]]
+ result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ vrf = "RED_B"
+ routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]]
+ result = verify_bgp_community(tgen, addr_type, "r2", routes, input_dict, vrf)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_match_traffic_based_on_vrf_p0(request):
+ """
+ FUNC_13:
+ Configure a route-map on DUT to match traffic based
+ on a VRF interfaces.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1 "
+ "in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure a route-map on R1 to match the prefixes "
+ "coming from vrf RED_A and set as-prepend to these routes."
+ )
+
+ input_dict_4 = {
+ "r1": {
+ "route_maps": {
+ "ABC": [
+ {
+ "action": "permit",
+ "match": {"source-vrf": "RED_A"},
+ "set": {"path": {"as_num": 1, "as_action": "prepend"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "On R1, import the routes form vrf RED_A and RED_B to BLUE_A and"
+ " apply the route-map under vrf BLUE_A while importing"
+ )
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp 100 vrf BLUE_A",
+ "address-family ipv4 unicast",
+ "import vrf RED_A",
+ "import vrf RED_B",
+ "import vrf route-map ABC",
+ "address-family ipv6 unicast",
+ "import vrf RED_A",
+ "import vrf RED_B",
+ "import vrf route-map ABC",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "All the prefixes advertised from RED_1 and BLUE_1 in vrfs "
+ "RED_B and BLUE_B must prepend the AS number in as-path on R2."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_7 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_vrf_lite_with_static_bgp_originated_routes_p0(request):
+ """
+ FUNC_14:
+ Test VRF-lite with Static+BGP originated routes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_3 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK5_1["ipv4"]]
+ + [NETWORK5_2["ipv4"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK5_1["ipv6"]]
+ + [NETWORK5_2["ipv6"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK6_1["ipv4"]]
+ + [NETWORK6_2["ipv4"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK6_1["ipv6"]]
+ + [NETWORK6_2["ipv6"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue1": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK7_1["ipv4"]]
+ + [NETWORK7_2["ipv4"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK7_1["ipv6"]]
+ + [NETWORK7_2["ipv6"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK8_1["ipv4"]]
+ + [NETWORK8_2["ipv4"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [NETWORK8_1["ipv6"]]
+ + [NETWORK8_2["ipv6"]]
+ }
+ ],
+ "redistribute": [{"redist_type": "static"}],
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Static routes must be installed in associated VRF" " table only.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "All the routers must receive advertised as well as "
+ "redistributed(static) prefixes in associated VRF tables."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_prefix_list_to_permit_deny_prefixes_p0(request):
+ """
+ FUNC_15:
+ Configure prefix-lists on DUT and apply to BGP peers to
+ permit/deny prefixes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify routes are present before applying prefix-list")
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On routers RED_1 and BLUE_1, configure prefix-lists to permit"
+ " 4 prefixes and deny 1 prefix x.x.x.5. Apply these in outbound"
+ "direction for each neighbour."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "red1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_red1_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ },
+ {
+ "seqid": 11,
+ "network": NETWORK2_1[addr_type],
+ "action": "permit",
+ },
+ {
+ "seqid": 12,
+ "network": NETWORK1_2[addr_type],
+ "action": "deny",
+ },
+ {
+ "seqid": 13,
+ "network": NETWORK2_2[addr_type],
+ "action": "deny",
+ },
+ ]
+ }
+ }
+ },
+ "blue1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_blue1_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ },
+ {
+ "seqid": 11,
+ "network": NETWORK2_1[addr_type],
+ "action": "permit",
+ },
+ {
+ "seqid": 12,
+ "network": NETWORK1_2[addr_type],
+ "action": "deny",
+ },
+ {
+ "seqid": 13,
+ "network": NETWORK2_2[addr_type],
+ "action": "deny",
+ },
+ ]
+ }
+ }
+ },
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_r1_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ },
+ {
+ "seqid": 11,
+ "network": NETWORK2_1[addr_type],
+ "action": "deny",
+ },
+ ]
+ }
+ }
+ },
+ }
+ result = create_prefix_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_5 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue1": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_blue1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_blue1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_blue1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_blue1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, each BGP neighbor receives 1"
+ " prefixes in routing table and drops (x.x.x.2)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ permitted_routes = {
+ "red1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"},
+ {"network": [NETWORK2_1[addr_type]], "vrf": "RED_B"},
+ ]
+ }
+ }
+
+ denied_routes = {
+ "red1": {
+ "static_routes": [
+ {"network": [NETWORK1_2[addr_type]], "vrf": "RED_A"},
+ {"network": [NETWORK2_2[addr_type]], "vrf": "RED_B"},
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, permitted_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n"
+ "{}:Expected behaviour: Routes are denied by prefix-list \nError {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On router R1, configure prefix-lists to permit 2 "
+ "prefixes(x.x.x.1-2) and deny 2 prefix(x.x.x.3-4). Apply"
+ " these in inbound direction for each neighbour."
+ )
+
+ input_dict_6 = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "prefix_lists": [
+ {
+ "name": "pflist_r1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, each BGP neighbor installs"
+ " only 1 prefix (x.x.x.1)."
+ )
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ permitted_routes = {
+ "red1": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "RED_A"}]
+ }
+ }
+
+ denied_routes = {
+ "red1": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED_A"}]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, permitted_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, denied_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nExpected behaviour: Routes are denied by prefix-list \nError {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_set_and_match_tag_p0(request):
+ """
+ FUNC_16_1:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 4001,
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 4001,
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure a route-maps to match tag")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "red1": {
+ "route_maps": {
+ "rmap1_{}".format(addr_type): [
+ {"action": "permit", "match": {addr_type: {"tag": "4001"}}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map")
+ input_dict_4 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "tag": 4001,
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \nError {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_set_and_match_metric_p0(request):
+ """
+ FUNC_16_2:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ },
+ ]
+ },
+ "blue1": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {"metric": 123},
+ }
+ ]
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure a route-maps to match tag")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "route_maps": {
+ "rmap1_{}".format(addr_type): [
+ {"action": "permit", "match": {"metric": 123}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map")
+ input_dict_4 = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that within vrf instances, BGP best path selection"
+ " algorithm remains intact and doesn't affect any other VRFs"
+ " routing decision."
+ )
+
+ dut = "r1"
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected Behavior: Routes are denied \nError {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_map_set_and_match_community_p0(request):
+ """
+ FUNC_16_3:
+ Configure a route-map on DUT to match traffic based various
+ match/set causes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise same set of BGP prefixes(IPv4+IPv6) from BLUE_1 and"
+ "BLUE_2 in vrf instances(BLUE_A and BLUE_B)"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Create community-list")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "rmap_lcomm_{}".format(addr_type),
+ "value": "1:1 1:2 1:3 1:4 1:5",
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure a route-maps to match tag")
+
+ step("Create route-maps in red1 and r1")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "red1": {
+ "route_maps": {
+ "rmap_red1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"community": {"num": "1:1 1:2 1:3 1:4 1:5"}},
+ }
+ ]
+ }
+ },
+ "r1": {
+ "route_maps": {
+ "rmap1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ "community_list": {"id": "rmap_lcomm_" + addr_type}
+ },
+ }
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure neighbor for route map")
+ input_dict_4 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_red1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "route_maps": [
+ {
+ "name": "rmap1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "All the prefixes advertised from RED_1 and BLUE_1 should carry"
+ " attributes set by outbound route-maps within specific vrfs. "
+ "Router R1 should be able to match and permit/deny those "
+ "prefixes based on received attributes. Please use below "
+ "commands to verify."
+ )
+
+ input_dict = {
+ "community": "1:1 1:2 1:3 1:4 1:5",
+ }
+
+ for addr_type in ADDR_TYPES:
+ vrf = "RED_A"
+ routes = [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]]
+ result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ vrf = "RED_B"
+ routes = [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]]
+ result = verify_bgp_community(tgen, addr_type, "r1", routes, input_dict, vrf)
+ assert result is True, "Test case {} : 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/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json
new file mode 100644
index 0000000..bcee7e1
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo2/bgp_multi_vrf_topo2.json
@@ -0,0 +1,1553 @@
+{
+ "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": {
+ "red1": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "red1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "blue1": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE_A",
+ "id": "1"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "blue1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r1": {
+ "links": {
+ "red1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "red1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "blue1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "blue1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link1":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link1":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link2":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red1": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link2":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link3":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link3":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link4":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue1": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r1-link4":
+ {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r4-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "red2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "red2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "blue2-link1": {"ipv4": "auto", "ipv6": "autor3", "vrf": "BLUE_A"},
+ "blue2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }],
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r1-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "red2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"}
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "blue2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"}
+ },
+ "vrfs":[
+ {
+ "name": "BLUE_A",
+ "id": "1"
+ },
+ {
+ "name": "BLUE_B",
+ "id": "2"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
new file mode 100644
index 0000000..04ebe61
--- /dev/null
+++ b/tests/topotests/bgp_multi_vrf_topo2/test_bgp_multi_vrf_topo2.py
@@ -0,0 +1,3852 @@
+#!/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 BGP Multi-VRF:
+
+CHAOS_1:
+ Do a shut and no shut on connecting interface of DUT,
+ to see if all vrf instances clear their respective BGP tables
+ during the interface down and restores when interface brought
+kCHAOS_3:
+ VRF leaking - next-hop interface is flapping.
+CHAOS_5:
+ VRF - VLANs - Routing Table ID - combination testcase
+ on DUT.
+CHAOS_9:
+ Verify that all vrf instances fall back
+ to backup path, if primary link goes down.
+CHAOS_6:
+ Restart BGPd daemon on DUT to check if all the
+ routes in respective vrfs are reinstalled..
+CHAOS_2:
+ Delete a VRF instance from DUT and check if the routes get
+ deleted from subsequent neighbour routers and appears again once VRF
+ is re-added.
+CHAOS_4:
+ Verify that VRF names are locally significant
+ to a router, and end to end connectivity depends on unique
+ virtual circuits (using VLANs or separate physical interfaces).
+CHAOS_8:
+ Restart all FRR services (reboot DUT) to check if all
+ the routes in respective vrfs are reinstalled.
+"""
+
+import os
+import sys
+import time
+import pytest
+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.topogen import Topogen, get_topogen
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import (
+ step,
+ verify_rib,
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ create_route_maps,
+ shutdown_bringup_interface,
+ start_router_daemons,
+ create_static_routes,
+ create_vrf_cfg,
+ create_interfaces_cfg,
+ create_interface_in_kernel,
+ get_frr_ipv6_linklocal,
+ check_router_status,
+ apply_raw_config,
+ required_linux_kernel_version,
+ kill_router_daemons,
+ start_router_daemons,
+ stop_router,
+ start_router,
+)
+
+from lib.topolog import logger
+from lib.bgp import clear_bgp, verify_bgp_rib, create_router_bgp, verify_bgp_convergence
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"}
+NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"}
+NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"}
+NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"}
+NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"}
+NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"}
+NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"}
+NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"}
+NETWORK9_1 = {"ipv4": "100.1.0.1/30", "ipv6": "100::1/126"}
+NETWORK9_2 = {"ipv4": "100.1.0.2/30", "ipv6": "100::2/126"}
+
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+LOOPBACK_2 = {
+ "ipv4": "20.20.20.20/32",
+ "ipv6": "20::20:20/128",
+}
+
+MAX_PATHS = 2
+KEEPALIVETIMER = 1
+HOLDDOWNTIMER = 3
+PREFERRED_NEXT_HOP = "link_local"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.14")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ 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...
+ json_file = "{}/bgp_multi_vrf_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_vrf_with_multiple_links_p1(request):
+ """
+ CHAOS_9:
+ Verify that all vrf instances fall back
+ to backup path, if primary link goes down.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Configure BGP neighborships(IPv4+IPv6) between R1 and R4 "
+ "using exact same link IPs for all 4 VRFs."
+ )
+
+ topo_modify = deepcopy(topo)
+ build_config_from_json(tgen, topo_modify)
+
+ interfaces = ["link1", "link2", "link3", "link4"]
+ for interface in interfaces:
+ topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][
+ "delete"
+ ] = True
+ topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][
+ "delete"
+ ] = True
+
+ step("Build interface config from json")
+ create_interfaces_cfg(tgen, topo_modify["routers"])
+
+ interfaces = ["link1", "link2", "link3", "link4"]
+ for interface in interfaces:
+ del topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)]["delete"]
+ del topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)]["delete"]
+
+ r1_config = []
+ r4_config = []
+ for addr_type in ADDR_TYPES:
+ interfaces = ["link1", "link2", "link3", "link4"]
+ for interface in interfaces:
+ intf_name_r1 = topo_modify["routers"]["r1"]["links"][
+ "r4-{}".format(interface)
+ ]["interface"]
+ topo_modify["routers"]["r1"]["links"]["r4-{}".format(interface)][
+ addr_type
+ ] = NETWORK9_1[addr_type]
+
+ intf_name_r4 = topo_modify["routers"]["r4"]["links"][
+ "r1-{}".format(interface)
+ ]["interface"]
+ topo_modify["routers"]["r4"]["links"]["r1-{}".format(interface)][
+ addr_type
+ ] = NETWORK9_2[addr_type]
+
+ r1_config.append("interface {}".format(intf_name_r1))
+ r4_config.append("interface {}".format(intf_name_r4))
+ if addr_type == "ipv4":
+ r1_config.append("no ip address {}".format(NETWORK9_1[addr_type]))
+ r4_config.append("no ip address {}".format(NETWORK9_2[addr_type]))
+ else:
+ r1_config.append("no ipv6 address {}".format(NETWORK9_1[addr_type]))
+ r4_config.append("no ipv6 address {}".format(NETWORK9_2[addr_type]))
+
+ step("Build interface config from json")
+ create_interfaces_cfg(tgen, topo_modify["routers"])
+
+ step("Create bgp config")
+ result = create_router_bgp(tgen, topo_modify["routers"])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify BGP convergence")
+
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "Advertise below prefixes in BGP using static redistribution"
+ " for both vrfs (RED_A and BLUE_A) on router R2.."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["r1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ VRFS = ["RED_A", "RED_B", "BLUE_A", "BLUE_B"]
+ AS_NUM = [100, 100, 100, 100]
+
+ 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_modify, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify routes are installed with same nexthop in different" " VRFs")
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ _input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ _input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ _input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+
+ R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ _input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ }
+ ]
+ }
+ }
+
+ R1_NEXTHOP = topo_modify["routers"]["r1"]["links"]["r4-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, _input_dict, next_hop=R1_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map on R3 to prepend as-path and apply"
+ " for neighbour router R2 in both vrfs, in inbound direction."
+ )
+
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "ASP": [
+ {
+ "action": "permit",
+ "set": {"path": {"as_num": 123, "as_action": "prepend"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Apply route-map to neighbours")
+ step(
+ "Configure ECMP on router R3 using 'max-path' command for both"
+ " VRFs RED_A and BLUE_A."
+ )
+
+ input_dict_5 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "route_maps": [
+ {"name": "ASP", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo_modify, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ peer = "r2"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A")
+ else:
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ intf = topo["routers"][peer]["links"]["r3-link3"]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A")
+ else:
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure ECMP on router R3 using max-path command for"
+ " both VRFs RED_A and BLUE_A."
+ )
+
+ input_dict_7 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": MAX_PATHS,
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo_modify, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("R3 should install prefixes from both next-hops (R2 and R4)")
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ peer = "r2"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ intf = topo_modify["routers"][peer]["links"]["r3-link1"]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="RED_A")
+ else:
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ intf = topo_modify["routers"][peer]["links"]["r3-link3"]["interface"]
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ R2_NEXTHOP = get_frr_ipv6_linklocal(tgen, peer, intf=intf, vrf="BLUE_A")
+ else:
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Shutdown interface between R2 and R3 for vrfs RED_A and " "BLUE_A.")
+
+ intf1 = topo_modify["routers"]["r2"]["links"]["r3-link1"]["interface"]
+ intf2 = topo_modify["routers"]["r2"]["links"]["r3-link3"]["interface"]
+
+ interfaces = [intf1, intf2]
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r2", intf, False)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ peer = "r4"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Unshut the interfaces between R2 and R3 for vrfs RED_A and BLUE_A.")
+
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r2", intf, True)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ peer = "r2"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove route-map from R3 for vrfs RED_A and BLUE_A.")
+
+ input_dict_6 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv4",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv6",
+ "direction": "in",
+ "delete": True,
+ },
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv4",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [
+ {
+ "name": "ASP_ipv6",
+ "direction": "in",
+ "delete": True,
+ },
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo_modify, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo_modify)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ R2_NEXTHOP = topo_modify["routers"]["r2"]["links"]["r3-link3"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R2_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Shutdown links between between R2 and R3 for vrfs RED_A and" " BLUE_A.")
+
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r2", intf, False)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link1"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ R4_NEXTHOP = topo_modify["routers"]["r4"]["links"]["r3-link3"][addr_type].split(
+ "/"
+ )[0]
+
+ result = verify_rib(tgen, addr_type, dut, input_dict, next_hop=R4_NEXTHOP)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bringup links between between R2 and R3 for vrfs RED_A and" " BLUE_A.")
+
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r2", intf, True)
+
+ step("Deleting manualy assigned ip address from router r1 and r4 interfaces")
+ raw_config = {"r1": {"raw_config": r1_config}, "r4": {"raw_config": r4_config}}
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_shut_noshut_p1(request):
+ """
+ CHAOS_1:
+ Do a shut and no shut on connecting interface of DUT,
+ to see if all vrf instances clear their respective BGP tables
+ during the interface down and restores when interface brought
+ back up again.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Build interface config from json")
+ create_interfaces_cfg(tgen, topo["routers"])
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ " for both vrfs (RED_A and RED_B) on router RED_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Api call to modify BGP timers")
+
+ input_dict_4 = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ 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:
+ clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"])
+
+ clear_bgp(tgen, addr_type, "r2", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"])
+
+ step("Shut down connecting interface between R1<<>>R2 on R1.")
+ step("Repeat step-3 and step-4 10 times.")
+
+ for count in range(1, 2):
+ step("Iteration {}".format(count))
+ step("Shut down connecting interface between R1<<>>R2 on R1.")
+
+ intf1 = topo["routers"]["r1"]["links"]["r2-link1"]["interface"]
+ intf2 = topo["routers"]["r1"]["links"]["r2-link2"]["interface"]
+ intf3 = topo["routers"]["r1"]["links"]["r2-link3"]["interface"]
+ intf4 = topo["routers"]["r1"]["links"]["r2-link4"]["interface"]
+
+ interfaces = [intf1, intf2, intf3, intf4]
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "On R2, all BGP peering in respective vrf instances go down"
+ " when the interface is shut"
+ )
+
+ step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nExpected Behaviour: BGP will not be converged \nError {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]]
+ + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]]
+ + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]]
+ + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]]
+ + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nExpected Behaviour: Routes are flushed out \nError {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nExpected Behaviour: Routes are flushed out \nError {}".format(
+ tc_name, result
+ )
+
+ step("Bring up connecting interface between R1<<>>R2 on R1.")
+ for intf in interfaces:
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "R2 restores BGP peering and routing tables in all vrf "
+ "instances when interface brought back up again"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]]
+ + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]]
+ + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]]
+ + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]]
+ + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_vrf_vlan_routing_table_p1(request):
+ """
+ CHAOS_5:
+ VRF - VLANs - Routing Table ID - combination testcase
+ on DUT.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique prefixes(IPv4+IPv6) in BGP using"
+ " network command for vrf RED_A on router R2"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that static routes(IPv4+IPv6) is overridden and doesn't"
+ " have duplicate entries within VRF RED_A on router RED-1"
+ )
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Api call to modify BGP timers")
+
+ input_dict_4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ 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:
+ clear_bgp(tgen, addr_type, "r3", vrf=["RED_A"])
+
+ step("Repeat for 5 times.")
+
+ for count in range(1, 2):
+ step("Iteration {}..".format(count))
+ step("Delete a specific VRF instance(RED_A) from router R3")
+
+ input_dict = {"r3": {"vrfs": [{"name": "RED_A", "id": "1", "delete": True}]}}
+
+ result = create_vrf_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Expected Behaviour: Routes are cleaned \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add/reconfigure the same VRF instance again")
+
+ result = create_vrf_cfg(tgen, {"r3": topo["routers"]["r3"]})
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After deleting VRFs ipv6 addresses will be deleted from kernel "
+ " Adding back ipv6 addresses"
+ )
+
+ dut = "r3"
+ vrf = "RED_A"
+
+ for c_link, c_data in topo["routers"][dut]["links"].items():
+ 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
+ )
+
+ step("Sleeping for {}+1 sec..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r3"
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK1_1[addr_type],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_vrf_route_leaking_next_hop_interface_flapping_p1(request):
+ """
+ CHAOS_3:
+ VRF leaking - next-hop interface is flapping.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Create loopback interface")
+
+ for addr_type in ADDR_TYPES:
+ create_interface_in_kernel(
+ tgen,
+ "red1",
+ "loopback2",
+ LOOPBACK_2[addr_type],
+ "RED_B",
+ )
+
+ intf_red1_r11 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"]
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {
+ "red1": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ },
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ step("VRF RED_A should install a route for vrf RED_B's " "loopback ip.")
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Repeat step-2 to 4 at least 5 times")
+
+ for count in range(1, 2):
+ intf1 = topo["routers"]["red1"]["links"]["r1-link2"]["interface"]
+
+ step(
+ "Iteration {}: Shutdown interface {} on router"
+ "RED_1.".format(count, intf1)
+ )
+ shutdown_bringup_interface(tgen, "red1", intf1, False)
+
+ step("Verify that RED_A removes static route from routing " "table.")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1, protocol="static", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n Expected Behaviour: Routes are"
+ " not present Error {}".format(tc_name, result)
+ )
+
+ step("Bring up interface {} on router RED_1 again.".format(intf1))
+ shutdown_bringup_interface(tgen, "red1", intf1, True)
+
+ step(
+ "Verify that RED_A reinstalls static route pointing to "
+ "RED_B's IP in routing table again"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red1"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": LOOPBACK_2[addr_type],
+ "interface": intf_red1_r11,
+ "nexthop_vrf": "RED_B",
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_restart_bgpd_daemon_p1(request):
+ """
+ CHAOS_6:
+ Restart BGPd daemon on DUT to check if all the
+ routes in respective vrfs are reinstalled..
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed\n Error {}".format(tc_name, result)
+
+ step("Kill BGPd daemon on R1.")
+ kill_router_daemons(tgen, "r1", ["bgpd"])
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are still present in VRF RED_A and RED_B \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are still present in VRF BLUE_A and BLUE_B \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("Bring up BGPd daemon on R1.")
+ start_router_daemons(tgen, "r1", ["bgpd"])
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_delete_and_re_add_vrf_p1(request):
+ """
+ CHAOS_2:
+ Delete a VRF instance from DUT and check if the routes get
+ deleted from subsequent neighbour routers and appears again once VRF
+ is re-added.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ "for both vrfs (RED_A and RED_B) on router RED_1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static for vrfs RED_A and RED_B and BLUE_A and BLUE_B")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verifying RIB and FIB before deleting VRFs")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Api call to modify BGP timers")
+
+ input_dict_4 = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ 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:
+ clear_bgp(tgen, addr_type, "r1", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"])
+
+ step("Delete vrfs RED_A and BLUE_A from R1.")
+
+ input_dict = {
+ "r1": {
+ "vrfs": [
+ {"name": "RED_A", "id": "1", "delete": True},
+ {"name": "BLUE_A", "id": "3", "delete": True},
+ ]
+ }
+ }
+
+ result = create_vrf_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "R2 must not receive the prefixes(in respective vrfs)"
+ "originated from RED_1 and BLUE_1."
+ )
+
+ step("Wait for {}+1 sec..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ },
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ },
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behaviour:"
+ " Routes are not present \n Error {}".format(tc_name, result)
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n Expected Behaviour:"
+ " Routes are not present \n Error {}".format(tc_name, result)
+ )
+
+ step("Add vrfs again RED_A and BLUE_A on R1.")
+
+ result = create_vrf_cfg(tgen, {"r1": topo["routers"]["r1"]})
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ create_interfaces_cfg(tgen, {"r1": topo["routers"]["r1"]})
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step(
+ "After deleting VRFs ipv6 addresses will be deleted from kernel "
+ " Adding back ipv6 addresses"
+ )
+
+ dut = "r1"
+ vrfs = ["RED_A", "BLUE_A"]
+
+ for vrf in vrfs:
+ for c_link, c_data in topo["routers"][dut]["links"].items():
+ 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
+ )
+
+ step(
+ "R2 should now receive the prefixes(in respective vrfs)"
+ "again. Check the debugging logs as well. For verification"
+ " use same commands as mention in step-3."
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {}: Failed\n Error {}".format(tc_name, result)
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_vrf_name_significance_p1(request):
+ """
+ CHAOS_4:
+ Verify that VRF names are locally significant
+ to a router, and end to end connectivity depends on unique
+ virtual circuits (using VLANs or separate physical interfaces).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ "for both vrfs (RED_A and RED_B) on router RED_1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique prefixes in BGP using static redistribution"
+ " for both vrfs (BLUE_A and BLUE_B) on router BLUE_1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static for vrfs RED_A and RED_B and BLUE_A and BLUE_B")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure allowas-in on red2 and blue2")
+
+ input_dict_4 = {
+ "red2": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue2": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "allowas-in": {"number_occurences": 2}
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verifying RIB and FIB before deleting VRFs")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue2"
+ input_dict_3 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ input_dict_4 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Api call to modify BGP timers")
+
+ input_dict_4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "red2": {
+ "bgp": [
+ {
+ "local_as": "500",
+ "vrf": "RED_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "500",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "red2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ "blue2": {
+ "bgp": [
+ {
+ "local_as": "800",
+ "vrf": "BLUE_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "800",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "blue2-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ },
+ }
+
+ 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:
+ clear_bgp(tgen, addr_type, "r3", vrf=["RED_A", "RED_B", "BLUE_A", "BLUE_B"])
+
+ clear_bgp(tgen, addr_type, "red2", vrf=["RED_A", "RED_B"])
+
+ clear_bgp(tgen, addr_type, "blue2", vrf=["BLUE_A", "BLUE_B"])
+
+ step("Delete vrfs RED_A and BLUE_A from R3")
+
+ input_dict = {
+ "r3": {
+ "vrfs": [
+ {"name": "RED_A", "id": "1", "delete": True},
+ {"name": "BLUE_A", "id": "3", "delete": True},
+ ]
+ }
+ }
+
+ result = create_vrf_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Waiting for {}+1..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ step("Verify RIB and FIB after deleting VRFs")
+
+ for addr_type in ADDR_TYPES:
+ dut = "red2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue2"
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n Expected Behaviour: Routes are not present \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Create 2 new VRFs PINK_A and GREY_A IN R3")
+
+ topo_modify = deepcopy(topo)
+ topo_modify["routers"]["r3"]["vrfs"][0]["name"] = "PINK_A"
+ topo_modify["routers"]["r3"]["vrfs"][0]["id"] = "1"
+ topo_modify["routers"]["r3"]["vrfs"][2]["name"] = "GREY_A"
+ topo_modify["routers"]["r3"]["vrfs"][2]["id"] = "3"
+
+ topo_modify["routers"]["r3"]["links"]["red2-link1"]["vrf"] = "PINK_A"
+ topo_modify["routers"]["r3"]["links"]["blue2-link1"]["vrf"] = "GREY_A"
+
+ topo_modify["routers"]["r3"]["links"]["r2-link1"]["vrf"] = "PINK_A"
+ topo_modify["routers"]["r3"]["links"]["r2-link3"]["vrf"] = "GREY_A"
+
+ topo_modify["routers"]["r3"]["links"]["r4-link1"]["vrf"] = "PINK_A"
+ topo_modify["routers"]["r3"]["links"]["r4-link3"]["vrf"] = "GREY_A"
+
+ topo_modify["routers"]["r3"]["bgp"][0]["vrf"] = "PINK_A"
+ topo_modify["routers"]["r3"]["bgp"][2]["vrf"] = "GREY_A"
+
+ result = create_vrf_cfg(tgen, {"r3": topo_modify["routers"]["r3"]})
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ create_interfaces_cfg(tgen, {"r3": topo_modify["routers"]["r3"]})
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = create_router_bgp(tgen, topo_modify["routers"])
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Api call to modify BGP timers")
+
+ input_dict_4 = {
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "200",
+ "vrf": "PINK_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "red2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREY_A",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE_B",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "blue2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": KEEPALIVETIMER,
+ "holddowntimer": HOLDDOWNTIMER,
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ },
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo_modify, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r3", vrf=["PINK_A", "RED_B", "GREY_A", "BLUE_B"])
+
+ step(
+ "After deleting VRFs ipv6 addresses will be deleted from kernel "
+ " Adding back ipv6 addresses"
+ )
+
+ dut = "r3"
+ vrfs = ["GREY_A", "PINK_A"]
+
+ for vrf in vrfs:
+ for c_link, c_data in topo_modify["routers"][dut]["links"].items():
+ 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
+ )
+
+ step("Waiting for {}+1 sec..".format(HOLDDOWNTIMER))
+ sleep(HOLDDOWNTIMER + 1)
+
+ step(
+ "Advertised prefixes should appear again in respective VRF"
+ " table on routers RED_2 and BLUE_2. Verify fib and rib entries"
+ )
+
+ for addr_type in ADDR_TYPES:
+ dut = "red2"
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ }
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ dut = "blue2"
+ input_dict_3 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ }
+ ]
+ }
+ }
+
+ input_dict_4 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_3)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_restart_frr_services_p1(request):
+ """
+ CHAOS_8:
+ Restart all FRR services (reboot DUT) to check if all
+ the routes in respective vrfs are reinstalled.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from RED_1"
+ " in vrf instances(RED_A and RED_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Advertise unique BGP prefixes(IPv4+IPv6) from BLUE_1 in"
+ " vrf instances(BLUE_A and BLUE_B)."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static..")
+
+ input_dict_3 = {}
+ for dut in ["red1", "blue1"]:
+ temp = {dut: {"bgp": []}}
+ input_dict_3.update(temp)
+
+ if "red" in dut:
+ VRFS = ["RED_A", "RED_B"]
+ AS_NUM = [500, 500]
+ elif "blue" in dut:
+ VRFS = ["BLUE_A", "BLUE_B"]
+ AS_NUM = [800, 800]
+
+ 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_3)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Restart frr on R1")
+ stop_router(tgen, "r1")
+ start_router(tgen, "r1")
+
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+
+ input_dict_1 = {
+ "red1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED_B",
+ },
+ ]
+ }
+ }
+
+ input_dict_2 = {
+ "blue1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_A",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "BLUE_B",
+ },
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_2)
+ 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/bgp_multiview_topo1/README.md b/tests/topotests/bgp_multiview_topo1/README.md
new file mode 100644
index 0000000..c1a1445
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/README.md
@@ -0,0 +1,125 @@
+# Simple FRRouting Route-Server Test
+
+## Topology
+ +----------+ +----------+ +----------+ +----------+ +----------+
+ | peer1 | | peer2 | | peer3 | | peer4 | | peer5 |
+ | AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 |
+ +-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+
+ | .1 | .2 | .3 | .4 | .5
+ | ______/ / / _________/
+ \ / ________________/ / /
+ | | / _________________________/ / +----------+
+ | | | / __________________________/ ___| peer6 |
+ | | | | / ____________________________/.6 | AS 65006 |
+ | | | | | / _________________________ +----------+
+ | | | | | | / __________________ \ +----------+
+ | | | | | | | / \ \___| peer7 |
+ | | | | | | | | \ .7 | AS 65007 |
+ ~~~~~~~~~~~~~~~~~~~~~ \ +----------+
+ ~~ SW1 ~~ \ +----------+
+ ~~ Switch ~~ \_____| peer8 |
+ ~~ 172.16.1.0/24 ~~ .8 | AS 65008 |
+ ~~~~~~~~~~~~~~~~~~~~~ +----------+
+ |
+ | .254
+ +---------+---------+
+ | FRR R1 |
+ | BGP Multi-View |
+ | Peer 1-3 > View 1 |
+ | Peer 4-5 > View 2 |
+ | Peer 6-8 > View 3 |
+ +---------+---------+
+ | .1
+ |
+ ~~~~~~~~~~~~~ Stub Network is redistributed
+ ~~ SW0 ~~ into each BGP view with different
+ ~~ 172.20.0.1/28 ~~ attributes (using route-map)
+ ~~ Stub Switch ~~
+ ~~~~~~~~~~~~~
+
+## FRR Configuration
+
+Full config as used is in r1 subdirectory
+
+Simplified `R1` config:
+
+ hostname r1
+ !
+ interface r1-stub
+ description Stub Network
+ ip address 172.20.0.1/28
+ no link-detect
+ !
+ interface r1-eth0
+ description to PE router - vlan1
+ ip address 172.16.1.254/24
+ no link-detect
+ !
+ router bgp 100 view 1
+ bgp router-id 172.30.1.1
+ network 172.20.0.0/28 route-map local1
+ timers bgp 60 180
+ neighbor 172.16.1.1 remote-as 65001
+ neighbor 172.16.1.2 remote-as 65002
+ neighbor 172.16.1.5 remote-as 65005
+ !
+ router bgp 100 view 2
+ bgp router-id 172.30.1.1
+ network 172.20.0.0/28 route-map local2
+ timers bgp 60 180
+ neighbor 172.16.1.3 remote-as 65003
+ neighbor 172.16.1.4 remote-as 65004
+ !
+ router bgp 100 view 3
+ bgp router-id 172.30.1.1
+ network 172.20.0.0/28
+ timers bgp 60 180
+ neighbor 172.16.1.6 remote-as 65006
+ neighbor 172.16.1.7 remote-as 65007
+ neighbor 172.16.1.8 remote-as 65008
+ !
+ route-map local1 permit 10
+ set community 100:9999 additive
+ set metric 0
+ !
+ route-map local2 permit 10
+ set as-path prepend 100 100 100 100 100
+ set community 100:1 additive
+ set metric 9999
+ !
+
+## Tests executed
+
+### Check if FRR is running
+
+Test is executed by running
+
+ vtysh -c "show logging" | grep "Logging configuration for"
+
+on router `R1`. This should return the logging information for all daemons registered
+to Zebra and the list of running daemons is compared to the daemons started for this
+test (`zebra` and `bgpd`)
+
+### Verify for BGP to converge
+
+BGP is expected to converge on each view within 60s total time. Convergence is verified by executing
+
+ vtysh -c "show ip bgp view 1 summary"
+ vtysh -c "show ip bgp view 2 summary"
+ vtysh -c "show ip bgp view 3 summary"
+
+and expecting 11 routes seen in the last column for each peer. (Each peer sends 11 routes)
+
+### Verifying BGP Routing Tables
+
+Routing table is verified by running
+
+ vtysh -c "show ip bgp view 1"
+ vtysh -c "show ip bgp view 2"
+ vtysh -c "show ip bgp view 3"
+
+and comparing the result against the stored table in the r1/show_ip_bgp_view_NN.ref files
+(with NN 1, 2, 3) (A few header and trailer lines are cut/adjusted ahead of the compare to
+adjust for different output based on recent changes)
+
+
diff --git a/tests/topotests/bgp_multiview_topo1/exabgp.env b/tests/topotests/bgp_multiview_topo1/exabgp.env
new file mode 100644
index 0000000..a328e04
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/exabgp.env
@@ -0,0 +1,54 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer1/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg
new file mode 100644
index 0000000..20e71c8
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer1/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 1 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 1";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.1;
+ local-address 172.16.1.1;
+ local-as 65001;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer2/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg
new file mode 100644
index 0000000..1e8eef1
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer2/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 2 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 2";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.2;
+ local-address 172.16.1.2;
+ local-as 65002;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer3/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg
new file mode 100644
index 0000000..ef1b249
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer3/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 3 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 3";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.3;
+ local-address 172.16.1.3;
+ local-as 65003;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer4/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg
new file mode 100644
index 0000000..7c50f73
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer4/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 4 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 4";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.4;
+ local-address 172.16.1.4;
+ local-as 65004;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer5/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg
new file mode 100644
index 0000000..22163c7
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer5/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 5 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 5";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.5;
+ local-address 172.16.1.5;
+ local-as 65005;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer6/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg
new file mode 100644
index 0000000..40b54c3
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer6/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 6 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 6";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.6;
+ local-address 172.16.1.6;
+ local-as 65006;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer7/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg
new file mode 100644
index 0000000..33312d0
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer7/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 7 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 7";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.7;
+ local-address 172.16.1.7;
+ local-as 65007;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py
new file mode 100755
index 0000000..09f6ea5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer8/exa-send.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+
+# Announce numRoutes different routes per PE
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.%s.%s.0/24 med 100 community %i:1 next-hop 172.16.1.%i\n"
+ % ((peer + 100), i, peer, peer)
+ )
+ stdout.flush()
+
+# Announce 1 overlapping route per peer
+stdout.write("announce route 10.0.1.0/24 med %i next-hop 172.16.1.%i\n" % (peer, peer))
+stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg
new file mode 100644
index 0000000..173ccb9
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/peer8/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 8 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 8";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 172.16.1.254 {
+ router-id 172.16.1.8;
+ local-address 172.16.1.8;
+ local-as 65008;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..cd7f44a
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/r1/bgpd.conf
@@ -0,0 +1,61 @@
+!
+! Zebra configuration saved from vty
+! 2015/12/24 21:46:33
+!
+log file bgpd.log
+!
+!debug bgp events
+!debug bgp keepalives
+!debug bgp updates
+!debug bgp fsm
+!debug bgp filters
+!debug bgp zebra
+!
+router bgp 100 view 1
+ bgp router-id 172.30.1.1
+ bgp always-compare-med
+ no bgp ebgp-requires-policy
+ network 172.20.0.0/28 route-map local1
+ timers bgp 60 180
+ neighbor 172.16.1.1 remote-as 65001
+ neighbor 172.16.1.1 timers 3 10
+ neighbor 172.16.1.2 remote-as 65002
+ neighbor 172.16.1.2 timers 3 10
+ neighbor 172.16.1.5 remote-as 65005
+ neighbor 172.16.1.5 timers 3 10
+!
+router bgp 100 view 2
+ bgp router-id 172.30.1.1
+ bgp always-compare-med
+ no bgp ebgp-requires-policy
+ network 172.20.0.0/28 route-map local2
+ timers bgp 60 180
+ neighbor 172.16.1.3 remote-as 65003
+ neighbor 172.16.1.3 timers 3 10
+ neighbor 172.16.1.4 remote-as 65004
+ neighbor 172.16.1.4 timers 3 10
+!
+router bgp 100 view 3
+ bgp router-id 172.30.1.1
+ bgp always-compare-med
+ no bgp ebgp-requires-policy
+ network 172.20.0.0/28
+ timers bgp 60 180
+ neighbor 172.16.1.6 remote-as 65006
+ neighbor 172.16.1.6 timers 3 10
+ neighbor 172.16.1.7 remote-as 65007
+ neighbor 172.16.1.7 timers 3 10
+ neighbor 172.16.1.8 remote-as 65008
+ neighbor 172.16.1.8 timers 3 10
+!
+route-map local1 permit 10
+ set community 100:9999 additive
+ set metric 0
+!
+route-map local2 permit 10
+ set as-path prepend 100 100 100 100 100
+ set community 100:1 additive
+ set metric 9999
+!
+line vty
+!
diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_1.json b/tests/topotests/bgp_multiview_topo1/r1/view_1.json
new file mode 100644
index 0000000..137b8a3
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/r1/view_1.json
@@ -0,0 +1,728 @@
+{
+ "vrfName": "1",
+ "routerId": "172.30.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 100,
+ "routes": {
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 5,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 2,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 1,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.0.0",
+ "prefixLen": 24,
+ "network": "10.101.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.1.0",
+ "prefixLen": 24,
+ "network": "10.101.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.2.0",
+ "prefixLen": 24,
+ "network": "10.101.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.3.0",
+ "prefixLen": 24,
+ "network": "10.101.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.4.0",
+ "prefixLen": 24,
+ "network": "10.101.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.5.0",
+ "prefixLen": 24,
+ "network": "10.101.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.6.0",
+ "prefixLen": 24,
+ "network": "10.101.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.7.0",
+ "prefixLen": 24,
+ "network": "10.101.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.8.0",
+ "prefixLen": 24,
+ "network": "10.101.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.101.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.101.9.0",
+ "prefixLen": 24,
+ "network": "10.101.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.1",
+ "path": "65001",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.0.0",
+ "prefixLen": 24,
+ "network": "10.102.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.1.0",
+ "prefixLen": 24,
+ "network": "10.102.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.2.0",
+ "prefixLen": 24,
+ "network": "10.102.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.3.0",
+ "prefixLen": 24,
+ "network": "10.102.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.4.0",
+ "prefixLen": 24,
+ "network": "10.102.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.5.0",
+ "prefixLen": 24,
+ "network": "10.102.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.6.0",
+ "prefixLen": 24,
+ "network": "10.102.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.7.0",
+ "prefixLen": 24,
+ "network": "10.102.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.8.0",
+ "prefixLen": 24,
+ "network": "10.102.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.102.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.102.9.0",
+ "prefixLen": 24,
+ "network": "10.102.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.2",
+ "path": "65002",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.2",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.0.0",
+ "prefixLen": 24,
+ "network": "10.105.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.1.0",
+ "prefixLen": 24,
+ "network": "10.105.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.2.0",
+ "prefixLen": 24,
+ "network": "10.105.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.3.0",
+ "prefixLen": 24,
+ "network": "10.105.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.4.0",
+ "prefixLen": 24,
+ "network": "10.105.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.5.0",
+ "prefixLen": 24,
+ "network": "10.105.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.6.0",
+ "prefixLen": 24,
+ "network": "10.105.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.7.0",
+ "prefixLen": 24,
+ "network": "10.105.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.8.0",
+ "prefixLen": 24,
+ "network": "10.105.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.105.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.105.9.0",
+ "prefixLen": 24,
+ "network": "10.105.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.5",
+ "path": "65005",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.5",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_2.json b/tests/topotests/bgp_multiview_topo1/r1/view_2.json
new file mode 100644
index 0000000..2ad28c5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/r1/view_2.json
@@ -0,0 +1,489 @@
+{
+ "vrfName": "2",
+ "routerId": "172.30.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 100,
+ "routes": {
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 4,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 3,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.0.0",
+ "prefixLen": 24,
+ "network": "10.103.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.1.0",
+ "prefixLen": 24,
+ "network": "10.103.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.2.0",
+ "prefixLen": 24,
+ "network": "10.103.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.3.0",
+ "prefixLen": 24,
+ "network": "10.103.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.4.0",
+ "prefixLen": 24,
+ "network": "10.103.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.5.0",
+ "prefixLen": 24,
+ "network": "10.103.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.6.0",
+ "prefixLen": 24,
+ "network": "10.103.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.7.0",
+ "prefixLen": 24,
+ "network": "10.103.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.8.0",
+ "prefixLen": 24,
+ "network": "10.103.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.103.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.103.9.0",
+ "prefixLen": 24,
+ "network": "10.103.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.3",
+ "path": "65003",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.3",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.0.0",
+ "prefixLen": 24,
+ "network": "10.104.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.1.0",
+ "prefixLen": 24,
+ "network": "10.104.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.2.0",
+ "prefixLen": 24,
+ "network": "10.104.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.3.0",
+ "prefixLen": 24,
+ "network": "10.104.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.4.0",
+ "prefixLen": 24,
+ "network": "10.104.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.5.0",
+ "prefixLen": 24,
+ "network": "10.104.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.6.0",
+ "prefixLen": 24,
+ "network": "10.104.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.7.0",
+ "prefixLen": 24,
+ "network": "10.104.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.8.0",
+ "prefixLen": 24,
+ "network": "10.104.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.104.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.104.9.0",
+ "prefixLen": 24,
+ "network": "10.104.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.4",
+ "path": "65004",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.4",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_multiview_topo1/r1/view_3.json b/tests/topotests/bgp_multiview_topo1/r1/view_3.json
new file mode 100644
index 0000000..d49694e
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/r1/view_3.json
@@ -0,0 +1,728 @@
+{
+ "vrfName": "3",
+ "routerId": "172.30.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 100,
+ "routes": {
+ "10.0.1.0/24": [
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 8,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "valid": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 7,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ },
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.0.1.0",
+ "prefixLen": 24,
+ "network": "10.0.1.0/24",
+ "metric": 6,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.0.0",
+ "prefixLen": 24,
+ "network": "10.106.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.1.0",
+ "prefixLen": 24,
+ "network": "10.106.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.2.0",
+ "prefixLen": 24,
+ "network": "10.106.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.3.0",
+ "prefixLen": 24,
+ "network": "10.106.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.4.0",
+ "prefixLen": 24,
+ "network": "10.106.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.5.0",
+ "prefixLen": 24,
+ "network": "10.106.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.6.0",
+ "prefixLen": 24,
+ "network": "10.106.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.7.0",
+ "prefixLen": 24,
+ "network": "10.106.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.8.0",
+ "prefixLen": 24,
+ "network": "10.106.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.106.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.106.9.0",
+ "prefixLen": 24,
+ "network": "10.106.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.6",
+ "path": "65006",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.6",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.0.0",
+ "prefixLen": 24,
+ "network": "10.107.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.1.0",
+ "prefixLen": 24,
+ "network": "10.107.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.2.0",
+ "prefixLen": 24,
+ "network": "10.107.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.3.0",
+ "prefixLen": 24,
+ "network": "10.107.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.4.0",
+ "prefixLen": 24,
+ "network": "10.107.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.5.0",
+ "prefixLen": 24,
+ "network": "10.107.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.6.0",
+ "prefixLen": 24,
+ "network": "10.107.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.7.0",
+ "prefixLen": 24,
+ "network": "10.107.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.8.0",
+ "prefixLen": 24,
+ "network": "10.107.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.107.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.107.9.0",
+ "prefixLen": 24,
+ "network": "10.107.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.7",
+ "path": "65007",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.7",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.0.0",
+ "prefixLen": 24,
+ "network": "10.108.0.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.1.0",
+ "prefixLen": 24,
+ "network": "10.108.1.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.2.0",
+ "prefixLen": 24,
+ "network": "10.108.2.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.3.0",
+ "prefixLen": 24,
+ "network": "10.108.3.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.4.0",
+ "prefixLen": 24,
+ "network": "10.108.4.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.5.0",
+ "prefixLen": 24,
+ "network": "10.108.5.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.6.0",
+ "prefixLen": 24,
+ "network": "10.108.6.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.7.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.7.0",
+ "prefixLen": 24,
+ "network": "10.108.7.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.8.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.8.0",
+ "prefixLen": 24,
+ "network": "10.108.8.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.108.9.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "10.108.9.0",
+ "prefixLen": 24,
+ "network": "10.108.9.0/24",
+ "metric": 100,
+ "weight": 0,
+ "peerId": "172.16.1.8",
+ "path": "65008",
+ "origin": "IGP",
+ "nexthops": [
+ {
+ "ip": "172.16.1.8",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_multiview_topo1/r1/zebra.conf b/tests/topotests/bgp_multiview_topo1/r1/zebra.conf
new file mode 100644
index 0000000..86343f5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/r1/zebra.conf
@@ -0,0 +1,23 @@
+!
+! Zebra configuration saved from vty
+! 2015/12/24 16:48:27
+!
+log file zebra.log
+!
+hostname r1
+!
+interface r1-stub
+ description Stub Network
+ ip address 172.20.0.1/28
+ no link-detect
+!
+interface r1-eth0
+ description to PE router - vlan1
+ ip address 172.16.1.254/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
new file mode 100644
index 0000000..77619e5
--- /dev/null
+++ b/tests/topotests/bgp_multiview_topo1/test_bgp_multiview_topo1.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_multiview_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 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.
+#
+
+r"""
+test_bgp_multiview_topo1.py: Simple FRR Route-Server Test
+
++----------+ +----------+ +----------+ +----------+ +----------+
+| peer1 | | peer2 | | peer3 | | peer4 | | peer5 |
+| AS 65001 | | AS 65002 | | AS 65003 | | AS 65004 | | AS 65005 |
++-----+----+ +-----+----+ +-----+----+ +-----+----+ +-----+----+
+ | .1 | .2 | .3 | .4 | .5
+ | ______/ / / _________/
+ \ / ________________/ / /
+ | | / _________________________/ / +----------+
+ | | | / __________________________/ ___| peer6 |
+ | | | | / ____________________________/.6 | AS 65006 |
+ | | | | | / _________________________ +----------+
+ | | | | | | / __________________ \ +----------+
+ | | | | | | | / \ \___| peer7 |
+ | | | | | | | | \ .7 | AS 65007 |
+ ~~~~~~~~~~~~~~~~~~~~~ \ +----------+
+ ~~ SW1 ~~ \ +----------+
+ ~~ Switch ~~ \_____| peer8 |
+ ~~ 172.16.1.0/24 ~~ .8 | AS 65008 |
+ ~~~~~~~~~~~~~~~~~~~~~ +----------+
+ |
+ | .254
+ +---------+---------+
+ | FRR R1 |
+ | BGP Multi-View |
+ | Peer 1-3 > View 1 |
+ | Peer 4-5 > View 2 |
+ | Peer 6-8 > View 3 |
+ +---------+---------+
+ | .1
+ |
+ ~~~~~~~~~~~~~ Stub Network is redistributed
+ ~~ SW0 ~~ into each BGP view with different
+ ~~ 172.20.0.1/28 ~~ attributes (using route-map)
+ ~~ Stub Switch ~~
+ ~~~~~~~~~~~~~
+"""
+
+import json
+import os
+import sys
+import pytest
+import json
+from time import sleep
+
+from functools import partial
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from lib import topotest
+from lib.topogen import get_topogen, Topogen
+from lib.common_config import step
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+fatal_error = ""
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ # Setup Routers
+ router = tgen.add_router("r1")
+
+ # Setup Provider BGP peers
+ peer = {}
+ for i in range(1, 9):
+ peer[i] = tgen.add_exabgp_peer(
+ "peer%s" % i, ip="172.16.1.%s/24" % i, defaultRoute="via 172.16.1.254"
+ )
+
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw0")
+ switch.add_link(router, nodeif="r1-stub")
+
+ # Second switch is for connection to all peering routers
+ switch = tgen.add_switch("sw1")
+ switch.add_link(router, nodeif="r1-eth0")
+ for j in range(1, 9):
+ switch.add_link(peer[j], nodeif="peer%s-eth0" % j)
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router = tgen.net["r1"]
+ router.loadConf("zebra", "%s/r1/zebra.conf" % thisDir)
+ router.loadConf("bgpd", "%s/r1/bgpd.conf" % thisDir)
+ tgen.gears["r1"].start()
+
+ # Starting PE Hosts and init ExaBGP on each of them
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ peer_dir = os.path.join(thisDir, pname)
+ env_file = os.path.join(thisDir, "exabgp.env")
+ peer.start(peer_dir, env_file)
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_router_running():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+def test_bgp_converge():
+ "Check for BGP converged on all peers and BGP views"
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Wait for BGP to converge (All Neighbors in either Full or TwoWay State)
+ step("Verify for BGP to converge")
+
+ timeout = 125
+ while timeout > 0:
+ print("Timeout in %s: " % timeout),
+ sys.stdout.flush()
+ # Look for any node not yet converged
+ for i in range(1, 2):
+ for view in range(1, 4):
+ notConverged = tgen.net["r%s" % i].cmd(
+ r'vtysh -c "show ip bgp view %s summary" 2> /dev/null | grep ^[0-9] | grep -vP " 11\s+(\d+)"'
+ % view
+ )
+ if notConverged:
+ print("Waiting for r%s, view %s" % (i, view))
+ sys.stdout.flush()
+ break
+ if notConverged:
+ break
+ if notConverged:
+ sleep(5)
+ timeout -= 5
+ else:
+ print("Done")
+ break
+ else:
+ # Bail out with error if a router fails to converge
+ bgpStatus = tgen.net["r%s" % i].cmd(
+ 'vtysh -c "show ip bgp view %s summary"' % view
+ )
+ assert False, "BGP did not converge:\n%s" % bgpStatus
+
+ tgen.routers_have_failure()
+
+
+def test_bgp_routingTable():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ step("Verifying BGP Routing Tables")
+
+ router = tgen.gears["r1"]
+ for view in range(1, 4):
+ json_file = "{}/{}/view_{}.json".format(thisDir, router.name, view)
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp view {} json".format(view),
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assertmsg = "Routing Table verification failed for router {}, view {}".format(
+ router.name, view
+ )
+ assert result is None, assertmsg
+
+ tgen.routers_have_failure()
+
+
+def test_shutdown_check_memleak():
+ tgen = get_topogen()
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
+
+
+if __name__ == "__main__":
+ # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/bgp_orf/__init__.py b/tests/topotests/bgp_orf/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_orf/__init__.py
diff --git a/tests/topotests/bgp_orf/r1/bgpd.conf b/tests/topotests/bgp_orf/r1/bgpd.conf
new file mode 100644
index 0000000..800bf1b
--- /dev/null
+++ b/tests/topotests/bgp_orf/r1/bgpd.conf
@@ -0,0 +1,9 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.2 capability orf prefix-list both
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_orf/r1/zebra.conf b/tests/topotests/bgp_orf/r1/zebra.conf
new file mode 100644
index 0000000..85ab531
--- /dev/null
+++ b/tests/topotests/bgp_orf/r1/zebra.conf
@@ -0,0 +1,8 @@
+!
+int lo
+ ip address 10.10.10.1/32
+ ip address 10.10.10.2/32
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_orf/r2/bgpd.conf b/tests/topotests/bgp_orf/r2/bgpd.conf
new file mode 100644
index 0000000..8c488aa
--- /dev/null
+++ b/tests/topotests/bgp_orf/r2/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 capability orf prefix-list both
+ neighbor 192.168.1.1 prefix-list r1 in
+ exit-address-family
+!
+ip prefix-list r1 seq 5 permit 10.10.10.1/32
diff --git a/tests/topotests/bgp_orf/r2/zebra.conf b/tests/topotests/bgp_orf/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/bgp_orf/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_orf/test_bgp_orf.py b/tests/topotests/bgp_orf/test_bgp_orf.py
new file mode 100644
index 0000000..cb74b85
--- /dev/null
+++ b/tests/topotests/bgp_orf/test_bgp_orf.py
@@ -0,0 +1,159 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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 if BGP ORF filtering is working correctly when modifying
+prefix-list.
+
+Initially advertise 10.10.10.1/32 from R1 to R2. Add new prefix
+10.10.10.2/32 to r1 prefix list on R2. Test if we updated ORF
+prefix-list correctly.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_orf():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge_r1():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {"advertisedRoutes": {"10.10.10.1/32": {}, "10.10.10.2/32": None}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't apply ORF from R1 to R2"
+
+ def _bgp_converge_r2():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast summary json"))
+ expected = {
+ "peers": {
+ "192.168.1.1": {
+ "pfxRcd": 1,
+ "pfxSnt": 1,
+ "state": "Established",
+ "peerState": "OK",
+ }
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "ORF filtering is not working from R1 to R2"
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ ip prefix-list r1 seq 10 permit 10.10.10.2/32
+ """
+ )
+
+ def _bgp_orf_changed_r1():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {"advertisedRoutes": {"10.10.10.1/32": {}, "10.10.10.2/32": {}}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_orf_changed_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't apply new ORF from R1 to R2"
+
+ def _bgp_orf_changed_r2():
+ output = json.loads(r2.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "10.10.10.1/32": [{"valid": True}],
+ "10.10.10.2/32": [{"valid": True}],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_orf_changed_r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "New ORF filtering is not working from R1 to R2"
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ no ip prefix-list r1 seq 10 permit 10.10.10.2/32
+ """
+ )
+
+ test_func = functools.partial(_bgp_converge_r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't apply initial ORF from R1 to R2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_path_attributes_topo1/__init__.py b/tests/topotests/bgp_path_attributes_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_path_attributes_topo1/__init__.py
diff --git a/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json b/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json
new file mode 100644
index 0000000..de2bffa
--- /dev/null
+++ b/tests/topotests/bgp_path_attributes_topo1/bgp_path_attributes.json
@@ -0,0 +1,363 @@
+{
+ "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":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }
+ ]
+ },
+ "bgp":{
+ "local_as":"555",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1": {"ipv4": "auto", "ipv6": "auto"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":{
+ "local_as":"555",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r5":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"555",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto"},
+ "r6": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp": {
+ "local_as": "666",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3": {"ipv4": "auto", "ipv6": "auto"},
+ "r7": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":{
+ "local_as":"666",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r7": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ },
+ "r7": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r6":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r4": {"ipv4": "auto", "ipv6": "auto"},
+ "r7": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":{
+ "local_as":"777",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {}
+ }
+ },
+ "r7": {
+ "dest_link": {
+ "r6": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r6": {}
+ }
+ },
+ "r7": {
+ "dest_link": {
+ "r6": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r7":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r5": {"ipv4": "auto", "ipv6": "auto"},
+ "r6": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":{
+ "local_as":"888",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r7": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r7": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r7": {}
+ }
+ },
+ "r6": {
+ "dest_link": {
+ "r7": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
new file mode 100644
index 0000000..870c2f7
--- /dev/null
+++ b/tests/topotests/bgp_path_attributes_topo1/test_bgp_path_attributes.py
@@ -0,0 +1,1541 @@
+#!/usr/bin/env python
+
+#
+# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Original work Copyright (c) 2018 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 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 AS-Path functionality:
+
+Setup module:
+- Create topology (setup module)
+- Bring up topology
+- Verify BGP convergence
+
+Test cases:
+1. Test next_hop attribute and verify best path is installed as per
+ reachable next_hop
+2. Test aspath attribute and verify best path is installed as per
+ shortest AS-Path
+3. Test localpref attribute and verify best path is installed as per
+ shortest local-preference
+4. Test weight attribute and and verify best path is installed as per
+ highest weight
+5. Test origin attribute and verify best path is installed as per
+ IGP>EGP>INCOMPLETE rule
+6. Test med attribute and verify best path is installed as per lowest
+ med value
+7. Test admin distance and verify best path is installed as per lowest
+ admin distance
+
+Teardown module:
+- Bring down the topology
+- stop routers
+
+"""
+
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ create_prefix_lists,
+ create_route_maps,
+ check_address_types,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_best_path_as_per_bgp_attribute,
+ verify_best_path_as_per_admin_distance,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Address read from env variables
+ADDR_TYPES = check_address_types()
+
+####
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ global ADDR_TYPES
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: %s", testsuite_run_time)
+ logger.info("=" * 40)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/bgp_path_attributes.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Checking BGP convergence
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ 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: %s", time.asctime(time.localtime(time.time())))
+ logger.info("=" * 40)
+
+
+#####################################################
+##
+## Testcases
+##
+#####################################################
+
+
+def test_next_hop_attribute(request):
+ """
+ Verifying route are not getting installed in, as next_hop is
+ unreachable, Making next hop reachable using next_hop_self
+ command and verifying routes are installed.
+ """
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r7": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r1"
+ protocol = "bgp"
+ # Verification should fail as nexthop-self is not enabled
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "{} routes are not present in RIB".format(
+ addr_type, tc_name
+ )
+
+ # Configure next-hop-self to bgp neighbor
+ input_dict_1 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r1"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_aspath_attribute(request):
+ "Verifying AS_PATH attribute functionality"
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r7": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "path"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify AS-Path and verify best path is changed
+ # Create Prefix list
+
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "200.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "200::/8",
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMAP_AS_PATH": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}},
+ "set": {"path": {"as_num": "111 222", "as_action": "prepend"}},
+ },
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"path": {"as_num": "111 222", "as_action": "prepend"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_AS_PATH",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_AS_PATH",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "path"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_localpref_attribute(request):
+ "Verifying LOCAL PREFERENCE attribute functionality"
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r7": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Prefix list
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "200.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "200::/8",
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMAP_LOCAL_PREF": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}},
+ "set": {"locPrf": 1111},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"locPrf": 1111},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "RMAP_LOCAL_PREF",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "RMAP_LOCAL_PREF",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "locPrf"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMAP_LOCAL_PREF": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}},
+ "set": {"locPrf": 50},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"locPrf": 50},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "locPrf"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_weight_attribute(request):
+ """
+ Test configure/modify weight attribute and
+ verify best path is installed as per highest weight
+ """
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r7": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "200.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "200::/8",
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMAP_WEIGHT": [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}},
+ "set": {"weight": 500},
+ },
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"weight": 500},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "RMAP_WEIGHT",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "weight"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify route map
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "RMAP_WEIGHT": [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_1_ipv4"}},
+ "set": {"weight": 1000},
+ },
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_1_ipv6"}},
+ "set": {"weight": 1000},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "weight"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r7": input_dict["r7"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_origin_attribute(request):
+ """
+ Test origin attribute and verify best path is
+ installed as per IGP>EGP>INCOMPLETE rule
+ """
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}}
+ }
+ }
+ },
+ }
+ }
+ },
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to create static routes
+ input_dict_3 = {
+ "r5": {
+ "static_routes": [
+ {"network": "200.50.2.0/32", "next_hop": "Null0"},
+ {"network": "200.60.2.0/32", "next_hop": "Null0"},
+ {"network": "200:50:2::/128", "next_hop": "Null0"},
+ {"network": "200:60:2::/128", "next_hop": "Null0"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "origin"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, {"r4": input_dict["r4"]}, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_med_attribute(request):
+ """
+ Test configure/modify MED attribute and verify best path
+ is installed as per lowest med value
+ """
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200.50.2.0/32"},
+ {"network": "200.60.2.0/32"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {"network": "200:50:2::/128"},
+ {"network": "200:60:2::/128"},
+ ]
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Prefix list
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_r2_ipv4": [
+ {
+ "seqid": 10,
+ "network": "200.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_r2_ipv6": [
+ {
+ "seqid": 20,
+ "network": "200::/8",
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ },
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_ls_r3_ipv4": [
+ {
+ "seqid": 10,
+ "network": "200.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_ls_r3_ipv6": [
+ {
+ "seqid": 20,
+ "network": "200::/8",
+ "le": "128",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ },
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "RMAP_MED_R2": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_r2_ipv4"}},
+ "set": {"metric": 100},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_r2_ipv6"}},
+ "set": {"metric": 100},
+ },
+ ]
+ }
+ },
+ "r3": {
+ "route_maps": {
+ "RMAP_MED_R3": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}},
+ "set": {"metric": 10},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}},
+ "set": {"metric": 10},
+ },
+ ]
+ }
+ },
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R2",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}},
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R2",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r1": {"dest_link": {"r2": {"next_hop_self": True}}},
+ }
+ }
+ },
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}},
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R3",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3": {"next_hop_self": True}}},
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "RMAP_MED_R3",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "metric"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify route-map to set med value
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "RMAP_MED_R3": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_ls_r3_ipv4"}},
+ "set": {"metric": 200},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_ls_r3_ipv6"}},
+ "set": {"metric": 200},
+ },
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "metric"
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_dict, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_admin_distance(request):
+ "Verifying admin distance functionality"
+
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Api call to create static routes
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "200.50.2.0/32",
+ "admin_distance": 80,
+ "next_hop": "10.0.0.14",
+ },
+ {
+ "network": "200.50.2.0/32",
+ "admin_distance": 60,
+ "next_hop": "10.0.0.18",
+ },
+ {
+ "network": "200:50:2::/128",
+ "admin_distance": 80,
+ "next_hop": "fd00::1",
+ },
+ {
+ "network": "200:50:2::/128",
+ "admin_distance": 60,
+ "next_hop": "fd00::1",
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying best path
+ dut = "r1"
+ attribute = "admin_distance"
+
+ input_dict = {
+ "ipv4": {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "200.50.2.0/32",
+ "admin_distance": 80,
+ "next_hop": "10.0.0.14",
+ },
+ {
+ "network": "200.50.2.0/32",
+ "admin_distance": 60,
+ "next_hop": "10.0.0.18",
+ },
+ ]
+ }
+ },
+ "ipv6": {
+ "r2": {
+ "static_routes": [
+ {
+ "network": "200:50:2::/128",
+ "admin_distance": 80,
+ "next_hop": "fd00::1",
+ },
+ {
+ "network": "200:50:2::/128",
+ "admin_distance": 60,
+ "next_hop": "fd00::1",
+ },
+ ]
+ }
+ },
+ }
+
+ for addr_type in ADDR_TYPES:
+ result = verify_best_path_as_per_admin_distance(
+ tgen, addr_type, dut, input_dict[addr_type], 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/bgp_peer_group/__init__.py b/tests/topotests/bgp_peer_group/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/__init__.py
diff --git a/tests/topotests/bgp_peer_group/r1/bgpd.conf b/tests/topotests/bgp_peer_group/r1/bgpd.conf
new file mode 100644
index 0000000..19b490a
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r1/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65001
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG timers 3 10
+ neighbor 192.168.255.3 peer-group PG
+ neighbor r1-eth0 interface peer-group PG
+!
diff --git a/tests/topotests/bgp_peer_group/r1/zebra.conf b/tests/topotests/bgp_peer_group/r1/zebra.conf
new file mode 100644
index 0000000..e2c399e
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_peer_group/r2/bgpd.conf b/tests/topotests/bgp_peer_group/r2/bgpd.conf
new file mode 100644
index 0000000..0880ee9
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r2/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65002
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG timers 3 10
+ neighbor r2-eth0 interface peer-group PG
+!
diff --git a/tests/topotests/bgp_peer_group/r2/zebra.conf b/tests/topotests/bgp_peer_group/r2/zebra.conf
new file mode 100644
index 0000000..606c17b
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_peer_group/r3/bgpd.conf b/tests/topotests/bgp_peer_group/r3/bgpd.conf
new file mode 100644
index 0000000..eb2fca1
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r3/bgpd.conf
@@ -0,0 +1,7 @@
+!
+router bgp 65003
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG timers 3 10
+ neighbor 192.168.255.1 peer-group PG
+!
diff --git a/tests/topotests/bgp_peer_group/r3/zebra.conf b/tests/topotests/bgp_peer_group/r3/zebra.conf
new file mode 100644
index 0000000..e9fdfb7
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.255.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_peer_group/test_bgp_peer-group.py b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
new file mode 100644
index 0000000..494f6c6
--- /dev/null
+++ b/tests/topotests/bgp_peer_group/test_bgp_peer-group.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if peer-group works for numbered and unnumbered configurations.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_peer_group():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_peer_group_configured():
+ output = json.loads(tgen.gears["r1"].vtysh_cmd("show ip bgp neighbor json"))
+ expected = {
+ "r1-eth0": {"peerGroup": "PG", "bgpState": "Established"},
+ "192.168.255.3": {"peerGroup": "PG", "bgpState": "Established"},
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_peer_group_configured)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(tgen.gears["r1"])
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env
new file mode 100644
index 0000000..6c554f5
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/exabgp.env
@@ -0,0 +1,53 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py
new file mode 100644
index 0000000..0f998c1
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python2
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, "r")
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg
new file mode 100644
index 0000000..4a7dc48
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer1/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer1.in";
+ encoder text;
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 1";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.2;
+ local-address 10.0.1.2;
+ local-as 64510;
+ peer-as 64510;
+ }
+
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py
new file mode 100644
index 0000000..0f998c1
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python2
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, "r")
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg
new file mode 100644
index 0000000..b53b054
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer2/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer2.in";
+ encoder text;
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 2";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.2.1 {
+ router-id 10.0.2.2;
+ local-address 10.0.2.2;
+ local-as 64511;
+ peer-as 64511;
+ }
+
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py
new file mode 100644
index 0000000..0f998c1
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python2
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, "r")
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg
new file mode 100644
index 0000000..6a1cc2f
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer3/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer3.in";
+ encoder text;
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 3";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.3.1 {
+ router-id 10.0.3.2;
+ local-address 10.0.3.2;
+ local-as 64502;
+ peer-as 64501;
+ }
+
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py
new file mode 100644
index 0000000..0f998c1
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exa_readpipe.py
@@ -0,0 +1,19 @@
+#!/usr/bin/env python2
+"Helper script to read api commands from a pipe and feed them to ExaBGP"
+
+import sys
+
+if len(sys.argv) != 2:
+ sys.exit(1)
+fifo = sys.argv[1]
+
+while True:
+ pipe = open(fifo, "r")
+ with pipe:
+ line = pipe.readline().strip()
+ if line != "":
+ sys.stdout.write("{}\n".format(line))
+ sys.stdout.flush()
+ pipe.close()
+
+sys.exit(0)
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg
new file mode 100644
index 0000000..2cc26cb
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/peer4/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa_readpipe.py /var/run/exabgp_peer4.in";
+ encoder text;
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 4";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.4.1 {
+ router-id 10.0.4.2;
+ local-address 10.0.4.2;
+ local-as 64503;
+ peer-as 64501;
+ }
+
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf
new file mode 100644
index 0000000..038f108
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/bgpd.conf
@@ -0,0 +1,16 @@
+!
+router bgp 64510
+ bgp router-id 10.0.1.1
+ no bgp ebgp-requires-policy
+ bgp confederation identifier 64501
+ bgp confederation peers 64511
+ bgp bestpath as-path multipath-relax
+ bgp bestpath compare-routerid
+ bgp bestpath peer-type multipath-relax
+ neighbor 10.0.1.2 remote-as 64510
+ neighbor 10.0.3.2 remote-as 64502
+ neighbor 10.0.4.2 remote-as 64503
+ neighbor 10.0.5.2 remote-as 64511
+!
+line vty
+!
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json
new file mode 100644
index 0000000..11dad78
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/multipath.json
@@ -0,0 +1,50 @@
+{
+ "routes": { "203.0.113.0/30": [
+ {
+ "valid":true,
+ "multipath":true,
+ "pathFrom":"external",
+ "peerId":"10.0.5.2"
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Peer Type",
+ "pathFrom":"external",
+ "peerId":"10.0.4.2"
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "pathFrom":"internal",
+ "peerId":"10.0.1.2"
+ }
+],"203.0.113.4/30": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Confed Peer Type",
+ "pathFrom":"external",
+ "peerId":"10.0.5.2"
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "pathFrom":"internal",
+ "peerId":"10.0.1.2"
+ }
+],"203.0.113.8/30": [
+ {
+ "valid":true,
+ "multipath":true,
+ "pathFrom":"external",
+ "peerId":"10.0.4.2"
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Router ID",
+ "pathFrom":"external",
+ "peerId":"10.0.3.2"
+ }
+] } }
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json
new file mode 100644
index 0000000..c621832
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/not-multipath.json
@@ -0,0 +1,50 @@
+{
+ "routes": { "203.0.113.0/30": [
+ {
+ "valid":true,
+ "multipath":null,
+ "pathFrom":"external",
+ "peerId":"10.0.5.2"
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Peer Type",
+ "pathFrom":"external",
+ "peerId":"10.0.4.2"
+ },
+ {
+ "valid":true,
+ "multipath":null,
+ "pathFrom":"internal",
+ "peerId":"10.0.1.2"
+ }
+],"203.0.113.4/30": [
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Confed Peer Type",
+ "pathFrom":"external",
+ "peerId":"10.0.5.2"
+ },
+ {
+ "valid":true,
+ "multipath":null,
+ "pathFrom":"internal",
+ "peerId":"10.0.1.2"
+ }
+],"203.0.113.8/30": [
+ {
+ "valid":true,
+ "multipath":true,
+ "pathFrom":"external",
+ "peerId":"10.0.4.2"
+ },
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"Router ID",
+ "pathFrom":"external",
+ "peerId":"10.0.3.2"
+ }
+] } }
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json
new file mode 100644
index 0000000..22ec2c2
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-confed.json
@@ -0,0 +1,33 @@
+{
+ "203.0.113.0\/30":[
+ {
+ "prefix":"203.0.113.0\/30",
+ "protocol":"bgp",
+ "installed":true,
+ "internalNextHopNum":4,
+ "internalNextHopActiveNum":4,
+ "nexthops":[
+ {
+ "ip":"198.51.100.2",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "active":true
+ },
+ {
+ "ip":"198.51.100.10",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json
new file mode 100644
index 0000000..facddcd
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-eBGP-iBGP.json
@@ -0,0 +1,33 @@
+{
+ "203.0.113.0\/30":[
+ {
+ "prefix":"203.0.113.0\/30",
+ "protocol":"bgp",
+ "installed":true,
+ "internalNextHopNum":4,
+ "internalNextHopActiveNum":4,
+ "nexthops":[
+ {
+ "ip":"198.51.100.1",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "active":true
+ },
+ {
+ "ip":"198.51.100.10",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json
new file mode 100644
index 0000000..5399cee
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-no-recursive.json
@@ -0,0 +1,35 @@
+{
+ "prefix":"203.0.113.0\/30",
+ "paths":[
+ {
+ "valid":false,
+ "peer":{
+ "peerId":"10.0.4.2",
+ "routerId":"10.0.4.2",
+ "type":"external"
+ }
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true,
+ "selectionReason":"Confed Peer Type"
+ },
+ "peer":{
+ "peerId":"10.0.5.2",
+ "routerId":"10.0.5.2",
+ "type":"confed-external"
+ }
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.1.2",
+ "routerId":"10.0.1.2",
+ "type":"confed-internal"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json
new file mode 100644
index 0000000..7da95ae
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1-recursive.json
@@ -0,0 +1,36 @@
+{
+ "prefix":"203.0.113.0\/30",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "bestpath":{
+ "overall":true,
+ "selectionReason":"Peer Type"
+ },
+ "peer":{
+ "peerId":"10.0.4.2",
+ "routerId":"10.0.4.2",
+ "type":"external"
+ }
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.5.2",
+ "routerId":"10.0.5.2",
+ "type":"confed-external"
+ }
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.1.2",
+ "routerId":"10.0.1.2",
+ "type":"confed-internal"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json
new file mode 100644
index 0000000..a90669a
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix1.json
@@ -0,0 +1,33 @@
+{
+ "prefix":"203.0.113.0\/30",
+ "paths":[
+ {
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.5.2",
+ "routerId":"10.0.5.2",
+ "type":"confed-external"
+ }
+ },
+ {
+ "multipath":true,
+ "bestpath":{
+ "overall":true,
+ "selectionReason":"Peer Type"
+ },
+ "peer":{
+ "peerId":"10.0.4.2",
+ "routerId":"10.0.4.2",
+ "type":"external"
+ }
+ },
+ {
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.1.2",
+ "routerId":"10.0.1.2",
+ "type":"confed-internal"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json
new file mode 100644
index 0000000..1bf38ef
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-ip-route.json
@@ -0,0 +1,23 @@
+{
+ "203.0.113.8\/30":[
+ {
+ "prefix":"203.0.113.8\/30",
+ "protocol":"bgp",
+ "installed":true,
+ "internalNextHopNum":2,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "active":true
+ },
+ {
+ "fib":null,
+ "ip":"198.51.100.10",
+ "active":null
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json
new file mode 100644
index 0000000..33d0f2d
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-no-recursive.json
@@ -0,0 +1,21 @@
+{
+ "prefix":"203.0.113.8\/30",
+ "paths":[
+ {
+ "valid":false,
+ "peer":{
+ "peerId":"10.0.4.2",
+ "routerId":"10.0.4.2",
+ "type":"external"
+ }
+ },
+ {
+ "valid":true,
+ "peer":{
+ "peerId":"10.0.3.2",
+ "routerId":"10.0.3.2",
+ "type":"external"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json
new file mode 100644
index 0000000..6ac2512
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/prefix3-recursive.json
@@ -0,0 +1,23 @@
+{
+ "prefix":"203.0.113.8\/30",
+ "paths":[
+ {
+ "valid":true,
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.4.2",
+ "routerId":"10.0.4.2",
+ "type":"external"
+ }
+ },
+ {
+ "valid":true,
+ "multipath":true,
+ "peer":{
+ "peerId":"10.0.3.2",
+ "routerId":"10.0.3.2",
+ "type":"external"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf
new file mode 100644
index 0000000..911aa1c
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r1/zebra.conf
@@ -0,0 +1,27 @@
+!
+hostname r1
+!
+interface r1-eth0
+ description ExaBGP iBGP peer1
+ ip address 10.0.1.1/24
+ no link-detect
+!
+interface r1-eth1
+ description ExaBGP peer3
+ ip address 10.0.3.1/24
+ no link-detect
+!
+interface r1-eth2
+ description ExaBGP peer4
+ ip address 10.0.4.1/24
+ no link-detect
+!
+interface r1-eth3
+ description r2 confed peer
+ ip address 10.0.5.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf
new file mode 100644
index 0000000..2362a19
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/bgpd.conf
@@ -0,0 +1,19 @@
+!
+!log file bgpd.log
+!
+router bgp 64511
+ bgp confederation identifier 64501
+ bgp confederation peers 64510
+ bgp router-id 10.0.5.2
+ no bgp ebgp-requires-policy
+ neighbor 10.0.2.2 remote-as 64511
+ neighbor 10.0.5.1 remote-as 64510
+ !
+ address-family ipv4 unicast
+ neighbor 10.0.5.1 route-map dropall in
+ exit-address-family
+!
+route-map dropall deny 10
+!
+line vty
+!
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf
new file mode 100644
index 0000000..35ebe0d
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/staticd.conf
@@ -0,0 +1,4 @@
+hostname r2
+!
+ip route 198.51.100.0/24 10.0.2.2
+!
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf
new file mode 100644
index 0000000..900e7d4
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/r2/zebra.conf
@@ -0,0 +1,19 @@
+!
+!
+hostname r2
+!
+interface r2-eth0
+ description ExaBGP peer
+ ip address 10.0.2.1/24
+ no link-detect
+!
+interface r2-eth1
+ description r1 confed peer
+ ip address 10.0.5.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
new file mode 100755
index 0000000..8321a57
--- /dev/null
+++ b/tests/topotests/bgp_peer_type_multipath_relax/test_bgp_peer-type_multipath-relax.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 Arista Networks, Inc.
+#
+# 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 Arista Networks DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_peer-type_multipath-relax.py:
+
+Test the effects of the "bgp bestpath peer-type multipath-relax" command
+
+- enabling the command allows eBGP, iBGP, and confed routes to be multipath
+- the choice of best path is not affected
+- disabling the command removes iBGP/confed routes from multipath
+- enabling the command does not forgive eBGP routes of the requirement
+ (when enabled) that next hops resolve over connected routes
+- a mixed-type multipath next hop, when published to zebra, does not
+ require resolving next hops over connected routes
+- with the command enabled, an all-eBGP multipath next hop still requires
+ resolving next hops over connected routes when published to zebra
+
+Topology used by the test:
+
+ eBGP +------+ iBGP
+ peer1 ---- | r1 | ---- peer3
+ | |
+peer2 ---- r2 ---- | | ---- peer4
+ iBGP confed +------+ eBGP
+
+r2 is present in this topology because ExaBGP does not currently support
+confederations so we use FRR to advertise the required AS_CONFED_SEQUENCE.
+
+Routes are advertised from different peers to form interesting multipaths.
+
+ peer1 peer2 peer3 peer4 multipath on r1
+
+203.0.113.0/30 x x x all 3
+203.0.113.4/30 x x confed-iBGP
+203.0.113.8/30 x x eBGP-only
+
+There is also a BGP-advertised route used only for recursively resolving
+next hops.
+"""
+
+import functools
+import json
+import os
+import pytest
+import sys
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Set up routers
+ tgen.add_router("r1") # DUT
+ tgen.add_router("r2")
+
+ # Set up peers
+ for peern in range(1, 5):
+ peer = tgen.add_exabgp_peer(
+ "peer{}".format(peern),
+ ip="10.0.{}.2/24".format(peern),
+ defaultRoute="via 10.0.{}.1".format(peern),
+ )
+ if peern == 2:
+ tgen.add_link(tgen.gears["r2"], peer)
+ else:
+ tgen.add_link(tgen.gears["r1"], peer)
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ router.run("/bin/bash {}/setup_vrfs".format(CWD))
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+ # Start up exabgp peers
+ peers = tgen.exabgp_peers()
+ for peer in peers:
+ fifo_in = "/var/run/exabgp_{}.in".format(peer)
+ if os.path.exists(fifo_in):
+ os.remove(fifo_in)
+ os.mkfifo(fifo_in, 0o777)
+ logger.info("Starting ExaBGP on peer {}".format(peer))
+ peer_dir = os.path.join(CWD, peer)
+ env_file = os.path.join(CWD, "exabgp.env")
+ peers[peer].start(peer_dir, env_file)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def test_bgp_peer_type_multipath_relax():
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def exabgp_cmd(peer, cmd):
+ pipe = open("/run/exabgp_{}.in".format(peer), "w")
+ with pipe:
+ pipe.write(cmd)
+ pipe.close()
+
+ # Prefixes used in the test
+ prefix1 = "203.0.113.0/30"
+ prefix2 = "203.0.113.4/30"
+ prefix3 = "203.0.113.8/30"
+ # Next hops used for iBGP/confed routes
+ resolved_nh1 = "198.51.100.1"
+ resolved_nh2 = "198.51.100.2"
+ # BGP route used for recursive resolution
+ bgp_resolving_prefix = "198.51.100.0/24"
+ # Next hop that will require non-connected recursive resolution
+ ebgp_resolved_nh = "198.51.100.10"
+
+ # Send a non-connected route to resolve others
+ exabgp_cmd(
+ "peer3", "announce route {} next-hop self\n".format(bgp_resolving_prefix)
+ )
+ router = tgen.gears["r1"]
+
+ # It seems that if you write to the exabgp socket too quickly in
+ # succession, requests get lost. So verify prefix1 now instead of
+ # after all the prefixes are advertised.
+ logger.info("Create and verify mixed-type multipaths")
+ exabgp_cmd(
+ "peer1",
+ "announce route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix1, resolved_nh1
+ ),
+ )
+ exabgp_cmd(
+ "peer2",
+ "announce route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix1, resolved_nh2
+ ),
+ )
+ exabgp_cmd("peer4", "announce route {} next-hop self\n".format(prefix1))
+ reffile = os.path.join(CWD, "r1/prefix1.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp {} json".format(prefix1),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Mixed-type multipath not found"
+ assert res is None, assertMsg
+
+ logger.info("Create and verify eBGP and iBGP+confed multipaths")
+ exabgp_cmd(
+ "peer1",
+ "announce route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix2, resolved_nh1
+ ),
+ )
+ exabgp_cmd(
+ "peer2",
+ "announce route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix2, resolved_nh2
+ ),
+ )
+ exabgp_cmd("peer3", "announce route {} next-hop self".format(prefix3))
+ exabgp_cmd("peer4", "announce route {} next-hop self".format(prefix3))
+ reffile = os.path.join(CWD, "r1/multipath.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Not all expected multipaths found"
+ assert res is None, assertMsg
+
+ logger.info("Toggle peer-type multipath-relax and verify the changes")
+ router.vtysh_cmd(
+ "conf\n router bgp 64510\n no bgp bestpath peer-type multipath-relax\n"
+ )
+ # This file verifies "multipath" is not set
+ reffile = os.path.join(CWD, "r1/not-multipath.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Disabling peer-type multipath-relax did not take effect"
+ assert res is None, assertMsg
+
+ router.vtysh_cmd(
+ "conf\n router bgp 64510\n bgp bestpath peer-type multipath-relax\n"
+ )
+ reffile = os.path.join(CWD, "r1/multipath.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip bgp json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Reenabling peer-type multipath-relax did not take effect"
+ assert res is None, assertMsg
+
+ logger.info("Check recursive resolution of eBGP next hops is not affected")
+ # eBGP next hop resolution rejects recursively resolved next hops by
+ # default, even with peer-type multipath-relax
+ exabgp_cmd(
+ "peer4", "announce route {} next-hop {}\n".format(prefix3, ebgp_resolved_nh)
+ )
+ reffile = os.path.join(CWD, "r1/prefix3-no-recursive.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp {} json".format(prefix3),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3)
+ assert res is None, assertMsg
+
+ exabgp_cmd(
+ "peer4", "announce route {} next-hop {}\n".format(prefix1, ebgp_resolved_nh)
+ )
+ reffile = os.path.join(CWD, "r1/prefix1-no-recursive.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp {} json".format(prefix1),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1)
+ assert res is None, assertMsg
+
+ # When other config allows recursively resolved eBGP next hops,
+ # such next hops in all-eBGP multipaths should be valid
+ router.vtysh_cmd("conf\n router bgp 64510\n neighbor 10.0.4.2 ebgp-multihop\n")
+ reffile = os.path.join(CWD, "r1/prefix3-recursive.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp {} json".format(prefix3),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix3)
+ assert res is None, assertMsg
+
+ reffile = os.path.join(CWD, "r1/prefix1-recursive.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip bgp {} json".format(prefix1),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "Recursive eBGP next hop not as expected for {}".format(prefix1)
+ assert res is None, assertMsg
+
+ logger.info("Check mixed-type multipath next hop recursive resolution in FIB")
+ # There are now two eBGP-learned routes with a recursively resolved next;
+ # hop; one is all-eBGP multipath, and the other is iBGP/eBGP/
+ # confed-external. The peer-type multipath-relax feature only enables
+ # recursive resolution in FIB if any next hop is iBGP/confed-learned. The
+ # all-eBGP multipath will have only one valid next hop in the FIB.
+ reffile = os.path.join(CWD, "r1/prefix3-ip-route.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route {} json".format(prefix3),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "FIB next hops mismatch for all-eBGP multipath"
+ assert res is None, assertMsg
+
+ # check confed-external enables recursively resolved next hops by itself
+ exabgp_cmd(
+ "peer1",
+ "withdraw route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix1, resolved_nh1
+ ),
+ )
+ reffile = os.path.join(CWD, "r1/prefix1-eBGP-confed.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route {} json".format(prefix1),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "FIB next hops mismatch for eBGP+confed-external multipath"
+ assert res is None, assertMsg
+
+ # check iBGP by itself
+ exabgp_cmd(
+ "peer1",
+ "announce route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix1, resolved_nh1
+ ),
+ )
+ exabgp_cmd(
+ "peer2",
+ "withdraw route {} next-hop {} as-path [ 64499 ]\n".format(
+ prefix1, resolved_nh2
+ ),
+ )
+ reffile = os.path.join(CWD, "r1/prefix1-eBGP-iBGP.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route {} json".format(prefix1),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertMsg = "FIB next hops mismatch for eBGP+iBGP multipath"
+ assert res is None, assertMsg
+
+
+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_prefix_list_any/__init__.py b/tests/topotests/bgp_prefix_list_any/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/__init__.py
diff --git a/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf
new file mode 100644
index 0000000..14c28ca
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/r1/bgpd.conf
@@ -0,0 +1,15 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ neighbor 2001:db8:1::2 remote-as external
+ address-family ipv4 unicast
+ network 192.168.0.1/32
+ no neighbor 2001:db8:1::2 activate
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor 2001:db8:1::2 activate
+ network 2001:db8::1/128
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_prefix_list_any/r1/zebra.conf b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf
new file mode 100644
index 0000000..c01a8cf
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/r1/zebra.conf
@@ -0,0 +1,5 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf
new file mode 100644
index 0000000..77f7c55
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/r2/bgpd.conf
@@ -0,0 +1,50 @@
+!
+debug bgp updates
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.1 remote-as external
+ neighbor 2001:db8:1::1 remote-as external
+ address-family ipv4 unicast
+ network 10.10.10.1/32
+ network 10.10.10.2/32
+ network 10.10.10.3/32
+ network 10.10.10.10/32
+ no neighbor 2001:db8:1::1 activate
+ neighbor 192.168.1.1 route-map r1-v4 out
+ exit-address-family
+ address-family ipv6 unicast
+ network 2001:db8:10::1/128
+ network 2001:db8:10::2/128
+ network 2001:db8:10::3/128
+ network 2001:db8:10::10/128
+ neighbor 2001:db8:1::1 activate
+ neighbor 2001:db8:1::1 route-map r1-v6 out
+ exit-address-family
+!
+ip prefix-list r1-1 seq 5 permit 10.10.10.1/32
+ip prefix-list r1-1 seq 10 permit 10.10.10.2/32
+ip prefix-list r1-1 seq 15 permit 10.10.10.3/32
+ip prefix-list r1-2 seq 5 permit 10.10.10.10/32
+!
+ipv6 prefix-list r1-1 seq 5 permit 2001:db8:10::1/128
+ipv6 prefix-list r1-1 seq 10 permit 2001:db8:10::2/128
+ipv6 prefix-list r1-1 seq 15 permit 2001:db8:10::3/128
+ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128
+!
+route-map r1-v4 permit 10
+ match ip address prefix-list r1-1
+exit
+!
+route-map r1-v4 permit 20
+ match ip address prefix-list r1-2
+exit
+!
+route-map r1-v6 permit 10
+ match ipv6 address prefix-list r1-1
+exit
+!
+route-map r1-v6 permit 20
+ match ipv6 address prefix-list r1-2
+exit
diff --git a/tests/topotests/bgp_prefix_list_any/r2/zebra.conf b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf
new file mode 100644
index 0000000..e90135c
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/r2/zebra.conf
@@ -0,0 +1,5 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py
new file mode 100644
index 0000000..0eb2447
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_any/test_bgp_prefix_list_any.py
@@ -0,0 +1,105 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+Test if route-map works correctly when modifying prefix-list
+from deny to permit with any, and vice-versa.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_prefix_list():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ def _bgp_prefixes_sent(count):
+ output = json.loads(r2.vtysh_cmd("show bgp summary json"))
+ expected = {
+ "ipv4Unicast": {
+ "peers": {"192.168.1.1": {"pfxSnt": count, "state": "Established"}}
+ },
+ "ipv6Unicast": {
+ "peers": {"2001:db8:1::1": {"pfxSnt": count, "state": "Established"}}
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_prefixes_sent, 4)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't converge initial topology"
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ ip prefix-list r1-2 seq 5 deny any
+ ipv6 prefix-list r1-2 seq 5 deny any
+ """
+ )
+
+ test_func = functools.partial(_bgp_prefixes_sent, 3)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Only 3 prefixes MUST be advertised, seeing more"
+
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ ip prefix-list r1-2 seq 5 permit 10.10.10.10/32
+ ipv6 prefix-list r1-2 seq 5 permit 2001:db8:10::10/128
+ """
+ )
+
+ test_func = functools.partial(_bgp_prefixes_sent, 4)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "More or less prefixes advertised to r1, MUST be 4"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_prefix_list_topo1/__init__.py b/tests/topotests/bgp_prefix_list_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/__init__.py
diff --git a/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json b/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json
new file mode 100644
index 0000000..3bb07ad
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/prefix_lists.json
@@ -0,0 +1,123 @@
+{
+ "address_types": ["ipv4"],
+ "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":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r4":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback", "add_static_route":"yes"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
new file mode 100644
index 0000000..2dc95ce
--- /dev/null
+++ b/tests/topotests/bgp_prefix_list_topo1/test_prefix_lists.py
@@ -0,0 +1,1336 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2019 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 prefix-list functionality:
+
+Test steps
+- Create topology (setup module)
+ Creating 4 routers topology, r1, r2, r3 are in IBGP and
+ r3, r4 are in EBGP
+- Bring up topology
+- Verify for bgp to converge
+
+IP prefix-list tests
+- Test ip prefix-lists IN permit
+- Test ip prefix-lists OUT permit
+- Test ip prefix-lists IN deny and permit any
+- Test delete ip prefix-lists
+- Test ip prefix-lists OUT deny and permit any
+- Test modify ip prefix-lists IN permit to deny
+- Test modify ip prefix-lists IN deny to permit
+- Test modify ip prefix-lists OUT permit to deny
+- Test modify prefix-lists OUT deny to permit
+- Test ip prefix-lists implicit deny
+"""
+
+import sys
+import time
+import os
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ create_prefix_lists,
+ verify_prefix_lists,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, clear_bgp_and_verify
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+# Global variables
+bgp_convergence = False
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/prefix_lists.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_ip_prefix_lists_in_permit(request):
+ """
+ Create ip prefix list and test permit prefixes IN direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [{"seqid": 10, "network": "any", "action": "permit"}]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure bgp neighbor with prefix list
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {"name": "pf_list_1", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ip_prefix_lists_out_permit(request):
+ """
+ Create ip prefix list and test permit prefixes out direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Static routes
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {"network": "20.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_5 = {
+ "r3": {
+ "static_routes": [
+ {"network": "10.0.0.2/30", "no_of_ip": 1, "next_hop": "10.0.0.9"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": 10, "network": "20.0.20.1/32", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ # Configure bgp neighbor with prefix list
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+ write_test_footer(tc_name)
+
+
+def test_ip_prefix_lists_in_deny_and_permit_any(request):
+ """
+ Create ip prefix list and test permit/deny prefixes IN direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 1, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+ # Create ip prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"},
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure bgp neighbor with prefix list
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {"name": "pf_list_1", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ # Configure prefix list to bgp neighbor
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_delete_prefix_lists(request):
+ """
+ Delete ip prefix list
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {"seqid": "10", "network": "10.0.20.1/32", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, input_dict_2)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Delete prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.20.1/32",
+ "action": "deny",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ip_prefix_lists_out_deny_and_permit_any(request):
+ """
+ Create ip prefix list and test deny/permit any prefixes OUT direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Static Routes
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+
+ # Create ip prefix list
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "deny",
+ },
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict_1, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+ write_test_footer(tc_name)
+
+
+def test_modify_prefix_lists_in_permit_to_deny(request):
+ """
+ Modify ip prefix list and test permit to deny prefixes IN direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_3 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {"name": "pf_list_1", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Modify prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "deny",
+ },
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_prefix_lists_in_deny_to_permit(request):
+ """
+ Modify ip prefix list and test deny to permit prefixes IN direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+
+ # Create ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "deny",
+ },
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {"name": "pf_list_1", "direction": "in"}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ # Modify ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_prefix_lists_out_permit_to_deny(request):
+ """
+ Modify ip prefix list and test permit to deny prefixes OUT direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+
+ # Create ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Modify ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "deny",
+ },
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_prefix_lists_out_deny_to_permit(request):
+ """
+ Modify ip prefix list and test deny to permit prefixes OUT direction
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+ # Create ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "deny",
+ },
+ {"seqid": "11", "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ # Modify ip prefix list
+ input_dict_1 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ip_prefix_lists_implicit_deny(request):
+ """
+ Create ip prefix list and test implicit deny
+ """
+
+ tgen = get_topogen()
+ if BGP_CONVERGENCE is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ # Create Static Routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": "10.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.2"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create Static Routes
+ input_dict_1 = {
+ "r2": {
+ "static_routes": [
+ {"network": "20.0.20.1/32", "no_of_ip": 9, "next_hop": "10.0.0.1"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to redistribute static routes
+ # Create ip prefix list
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": "10",
+ "network": "10.0.0.0/8",
+ "le": "32",
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_1, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Routes still" " present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_prefix_sid/__init__.py b/tests/topotests/bgp_prefix_sid/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/__init__.py
diff --git a/tests/topotests/bgp_prefix_sid/exabgp.env b/tests/topotests/bgp_prefix_sid/exabgp.env
new file mode 100644
index 0000000..6c554f5
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/exabgp.env
@@ -0,0 +1,53 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg
new file mode 100644
index 0000000..5b55366
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/peer1/exabgp.cfg
@@ -0,0 +1,103 @@
+group controller {
+ neighbor 10.0.0.1 {
+ router-id 10.0.0.101;
+ local-address 10.0.0.101;
+ local-as 2;
+ peer-as 1;
+
+ family {
+ ipv4 nlri-mpls;
+ }
+
+ static {
+ # ref: draft-ietf-idr-bgp-prefix-sid-27
+ #
+ # IANA temporarily assigned the following:
+ # attribute code type (suggested value: 40) to
+ # the BGP Prefix-SID attribute
+ #
+ # 0 1 2 3
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | Type | Length | RESERVED |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | Flags | Label Index |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | Label Index |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # Figure. Label-Index TLV (Prefix-SID type-1)
+ #
+ # 0 1 2 3
+ # 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | Type | Length | Flags |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | Flags |
+ # +-+-+-+-+-+-+-+-+
+ #
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | SRGB 1 (6 octets) |
+ # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ #
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | SRGB n (6 octets) |
+ # | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ # | |
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<Paste>
+ # Figure. Originator SRGB TLV (Prefix-SID type-3)
+
+ # ExaBGP generic-attribute binary pattern:
+ # Attribute-type: 0x28 (40:BGP_PREFIX_SID)
+ # Attribute-flag: 0xc0 (Option, Transitive)
+ # Attribute-body: Label-Index TLV and Originator SRGB TLV
+ # Label-Index TLV: 0x01000700000000000001
+ # Type (08bit): 0x01
+ # Length (16bit): 0x0007
+ # RESERVED (08bit): 0x00
+ # Flags (16bit): 0x0000
+ # Label Index (32bit): 0x00000001
+ # Originator SRGB TLV: 0x03000800000c350000000a
+ # Type (08bit): 0x03
+ # Length (16bit): 0x0008 (nb-SRGB is 1)
+ # Flags (16bit): 0x0000
+ # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1)
+ route 3.0.0.1/32 next-hop 10.0.0.101 label [800001] attribute [0x28 0xc0 0x0100070000000000000103000800000c350000000a];
+
+ # ExaBGP generic-attribute binary pattern:
+ # Attribute-type: 0x28 (40:BGP_PREFIX_SID)
+ # Attribute-flag: 0xc0 (Option, Transitive)
+ # Attribute-body: Label-Index TLV and Originator SRGB TLV
+ # Label-Index TLV: 0x01000700000000000001
+ # Type (08bit): 0x01
+ # Length (16bit): 0x0007
+ # RESERVED (08bit): 0x00
+ # Flags (16bit): 0x0000
+ # Label Index (32bit): 0x00000002
+ # Originator SRGB TLV: 0x03000800000c350000000a
+ # Type (08bit): 0x03
+ # Length (16bit): 0x0008 (nb-SRGB is 1)
+ # Flags (16bit): 0x0000
+ # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1)
+ route 3.0.0.2/32 next-hop 10.0.0.101 label [800002] attribute [0x28 0xc0 0x0100070000000000000203000800000c350000000a];
+
+ # ExaBGP generic-attribute binary pattern:
+ # Attribute-type: 0x28 (40:BGP_PREFIX_SID)
+ # Attribute-flag: 0xc0 (Option, Transitive)
+ # Attribute-body: Label-Index TLV and Originator SRGB TLV
+ # Label-Index TLV: 0x01000700000000000001
+ # Type (08bit): 0x01
+ # Length (16bit): 0x0007
+ # RESERVED (08bit): 0x00
+ # Flags (16bit): 0x0000
+ # Label Index (32bit): 0x00000003
+ # Originator SRGB TLV: 0x03000800000c350000000a
+ # Type (08bit): 0x03
+ # Length (16bit): 0x0008 (nb-SRGB is 1)
+ # Flags (16bit): 0x0000
+ # SRGB1 (48bit): 0x0c3500:0x00000a (800000-800010 is SRGB1)
+ route 3.0.0.3/32 next-hop 10.0.0.101 label [800003] attribute [0x28 0xc0 0x0100070000000000000303000800000c350000000a];
+ }
+ }
+}
diff --git a/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg
new file mode 100644
index 0000000..379d0a3
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/peer2/exabgp.cfg
@@ -0,0 +1,19 @@
+group controller {
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py --no-timestamp 2";
+ receive-routes;
+ encoder json;
+ }
+
+ neighbor 10.0.0.1 {
+ router-id 10.0.0.102;
+ local-address 10.0.0.102;
+ local-as 3;
+ peer-as 1;
+
+ family {
+ ipv4 nlri-mpls;
+ }
+ }
+}
diff --git a/tests/topotests/bgp_prefix_sid/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
new file mode 100644
index 0000000..e02226f
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/r1/bgpd.conf
@@ -0,0 +1,17 @@
+log stdout notifications
+log commands
+!
+router bgp 1
+ bgp router-id 10.0.0.1
+ no bgp default ipv4-unicast
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.101 remote-as 2
+ neighbor 10.0.0.101 timers 3 10
+ neighbor 10.0.0.102 remote-as 3
+ neighbor 10.0.0.102 timers 3 10
+ !
+ address-family ipv4 labeled-unicast
+ neighbor 10.0.0.101 activate
+ neighbor 10.0.0.102 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_prefix_sid/r1/zebra.conf b/tests/topotests/bgp_prefix_sid/r1/zebra.conf
new file mode 100644
index 0000000..0cd2605
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/r1/zebra.conf
@@ -0,0 +1,7 @@
+hostname r1
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ no shutdown
+!
+line vty
diff --git a/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
new file mode 100644
index 0000000..d51dc5f
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid/test_bgp_prefix_sid.py
@@ -0,0 +1,185 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_prefix_sid.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by LINE Corporation
+# Copyright (c) 2020 by Hiroki Shirokura <slank.dev@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_prefix_sid.py: Test BGP topology with EBGP on prefix-sid
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ router = tgen.add_router("r1")
+ switch = tgen.add_switch("s1")
+ switch.add_link(router)
+
+ switch = tgen.gears["s1"]
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.101", defaultRoute="via 10.0.0.1")
+ peer2 = tgen.add_exabgp_peer("peer2", ip="10.0.0.102", defaultRoute="via 10.0.0.1")
+ switch.add_link(peer1)
+ switch.add_link(peer2)
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1"))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))
+ )
+ router.start()
+
+ logger.info("starting exaBGP on peer1")
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ peer_dir = os.path.join(CWD, pname)
+ env_file = os.path.join(CWD, "exabgp.env")
+ logger.info("Running ExaBGP peer")
+ peer.start(peer_dir, env_file)
+ logger.info(pname)
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_r1_receive_and_advertise_prefix_sid_type1():
+ tgen = get_topogen()
+ router = tgen.gears["r1"]
+
+ def _check_type1_r1(router, prefix, remoteLabel, labelIndex):
+ output = router.vtysh_cmd(
+ "show bgp ipv4 labeled-unicast {} json".format(prefix)
+ )
+ output = json.loads(output)
+ expected = {
+ "prefix": prefix,
+ "advertisedTo": {"10.0.0.101": {}, "10.0.0.102": {}},
+ "paths": [
+ {
+ "valid": True,
+ "remoteLabel": remoteLabel,
+ "labelIndex": labelIndex,
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_check_type1_r1, router, "3.0.0.1/32", 800001, 1)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router)
+
+ test_func = functools.partial(_check_type1_r1, router, "3.0.0.2/32", 800002, 2)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result is None, 'Failed _check_type1_r1 in "{}"'.format(router)
+
+
+def exabgp_get_update_prefix(filename, afi, nexthop, prefix):
+ with open(filename) as f:
+ for line in f.readlines():
+ output = json.loads(line)
+ ret = output.get("neighbor")
+ if ret is None:
+ continue
+ ret = ret.get("message")
+ if ret is None:
+ continue
+ ret = ret.get("update")
+ if ret is None:
+ continue
+ ret = ret.get("announce")
+ if ret is None:
+ continue
+ ret = ret.get(afi)
+ if ret is None:
+ continue
+ ret = ret.get(nexthop)
+ if ret is None:
+ continue
+ ret = ret.get(prefix)
+ if ret is None:
+ continue
+ return output
+ return "Not found"
+
+
+def test_peer2_receive_prefix_sid_type1():
+ tgen = get_topogen()
+ peer2 = tgen.gears["peer2"]
+ logfile = "{}/{}-received.log".format(peer2.gearlogdir, peer2.name)
+
+ def _check_type1_peer2(prefix, labelindex):
+ output = exabgp_get_update_prefix(
+ logfile, "ipv4 nlri-mpls", "10.0.0.101", prefix
+ )
+ expected = {
+ "type": "update",
+ "neighbor": {
+ "ip": "10.0.0.1",
+ "message": {
+ "update": {
+ "attribute": {
+ "attribute-0x28-0xE0": "0x010007000000{:08x}".format(
+ labelindex
+ )
+ },
+ "announce": {"ipv4 nlri-mpls": {"10.0.0.101": {}}},
+ }
+ },
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_check_type1_peer2, "3.0.0.1/32", labelindex=1)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2")
+
+ test_func = functools.partial(_check_type1_peer2, "3.0.0.2/32", labelindex=2)
+ success, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result is None, 'Failed _check_type1_peer2 in "{}"'.format("peer2")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ ret = pytest.main(args)
+ sys.exit(ret)
diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg
new file mode 100644
index 0000000..3819179
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.cfg
@@ -0,0 +1,29 @@
+group controller {
+ neighbor 10.0.0.1 {
+ router-id 10.0.0.101;
+ local-address 10.0.0.101;
+ local-as 2;
+ peer-as 1;
+
+ family {
+ ipv6 mpls-vpn;
+ }
+
+ static {
+ route 2001:1::/64 {
+ rd 2:10;
+ next-hop 2001::2;
+ extended-community [ target:2:10 ];
+ label 3;
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
+ }
+ route 2001:2::/64 {
+ rd 2:10;
+ next-hop 2001::2;
+ extended-community [ target:2:10 ];
+ label 3;
+ attribute [0x28 0xc0 0x050019000100150020010db800010001000000000000000100ffff00 ];
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env
new file mode 100644
index 0000000..6c554f5
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/peer1/exabgp.env
@@ -0,0 +1,53 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf
new file mode 100644
index 0000000..ddc1f07
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/r1/bgpd.conf
@@ -0,0 +1,26 @@
+log stdout notifications
+log monitor notifications
+!log commands
+!
+!debug bgp zebra
+!debug bgp neighbor-events
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 1
+ bgp router-id 10.0.0.1
+ no bgp default ipv4-unicast
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.101 remote-as 2
+ neighbor 10.0.0.101 timers 3 10
+ !
+ address-family ipv6 vpn
+ neighbor 10.0.0.101 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json
new file mode 100644
index 0000000..42293b1
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry1.json
@@ -0,0 +1,50 @@
+{
+ "2:10":{
+ "prefix":"2001:1::\/64",
+ "advertisedTo":{
+ "10.0.0.101":{
+ }
+ },
+ "paths":[
+ {
+ "aspath":{
+ "string":"2",
+ "segments":[
+ {
+ "type":"as-sequence",
+ "list":[
+ 2
+ ]
+ }
+ ],
+ "length":1
+ },
+ "origin":"IGP",
+ "valid":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"RT:2:10"
+ },
+ "remoteLabel":3,
+ "remoteSid":"2001:db8:1:1::1",
+ "nexthops":[
+ {
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "scope":"global",
+ "metric":0,
+ "accessible":true,
+ "used":true
+ }
+ ],
+ "peer":{
+ "peerId":"10.0.0.101",
+ "routerId":"10.0.0.101",
+ "type":"external"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json
new file mode 100644
index 0000000..c9ad871
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/r1/vpnv6_rib_entry2.json
@@ -0,0 +1,50 @@
+{
+ "2:10":{
+ "prefix":"2001:2::\/64",
+ "advertisedTo":{
+ "10.0.0.101":{
+ }
+ },
+ "paths":[
+ {
+ "aspath":{
+ "string":"2",
+ "segments":[
+ {
+ "type":"as-sequence",
+ "list":[
+ 2
+ ]
+ }
+ ],
+ "length":1
+ },
+ "origin":"IGP",
+ "valid":true,
+ "bestpath":{
+ "overall":true
+ },
+ "extendedCommunity":{
+ "string":"RT:2:10"
+ },
+ "remoteLabel":3,
+ "remoteSid":"2001:db8:1:1::1",
+ "nexthops":[
+ {
+ "ip":"2001::2",
+ "afi":"ipv6",
+ "scope":"global",
+ "metric":0,
+ "accessible":true,
+ "used":true
+ }
+ ],
+ "peer":{
+ "peerId":"10.0.0.101",
+ "routerId":"10.0.0.101",
+ "type":"external"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_prefix_sid2/r1/zebra.conf b/tests/topotests/bgp_prefix_sid2/r1/zebra.conf
new file mode 100644
index 0000000..0cd2605
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/r1/zebra.conf
@@ -0,0 +1,7 @@
+hostname r1
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ no shutdown
+!
+line vty
diff --git a/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
new file mode 100755
index 0000000..96c4b66
--- /dev/null
+++ b/tests/topotests/bgp_prefix_sid2/test_bgp_prefix_sid2.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_prefix_sid2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by LINE Corporation
+# Copyright (c) 2020 by Hiroki Shirokura <slank.dev@gmail.com>
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_prefix_sid2.py: Test BGP topology with EBGP on prefix-sid
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ router = tgen.add_router("r1")
+ switch = tgen.add_switch("s1")
+ switch.add_link(router)
+
+ switch = tgen.gears["s1"]
+ peer1 = tgen.add_exabgp_peer("peer1", ip="10.0.0.101", defaultRoute="via 10.0.0.1")
+ switch.add_link(peer1)
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format("r1"))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))
+ )
+ router.start()
+
+ logger.info("starting exaBGP")
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ logger.info("starting exaBGP on {}".format(pname))
+ peer_dir = os.path.join(CWD, pname)
+ env_file = os.path.join(CWD, pname, "exabgp.env")
+ logger.info("Running ExaBGP peer on {}".format(pname))
+ peer.start(peer_dir, env_file)
+ logger.info(pname)
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def test_r1_rib():
+ def _check(name, cmd, expected_file):
+ logger.info("polling")
+ tgen = get_topogen()
+ router = tgen.gears[name]
+ output = json.loads(router.vtysh_cmd(cmd))
+ expected = open_json_file("{}/{}".format(CWD, expected_file))
+ return topotest.json_cmp(output, expected)
+
+ def check(name, cmd, expected_file):
+ logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+ check("r1", "show bgp ipv6 vpn 2001:1::/64 json", "r1/vpnv6_rib_entry1.json")
+ check("r1", "show bgp ipv6 vpn 2001:2::/64 json", "r1/vpnv6_rib_entry2.json")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ ret = pytest.main(args)
+ sys.exit(ret)
diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json
new file mode 100644
index 0000000..52995a0
--- /dev/null
+++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/bgp_recursive_route_ebgp_multi_hop.json
@@ -0,0 +1,321 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py
new file mode 100644
index 0000000..04f866f
--- /dev/null
+++ b/tests/topotests/bgp_recursive_route_ebgp_multi_hop/test_bgp_recursive_route_ebgp_multi_hop.py
@@ -0,0 +1,2420 @@
+#!/usr/bin/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 bgp recursive route and ebgp
+multi-hop functionality:
+
+1. Verify that BGP routes are installed in iBGP peer, only when there
+ is a recursive route for next-hop reachability.
+2. Verify that any BGP prefix received with next hop as self-ip is
+ not installed in BGP RIB or FIB table.
+3. Verify password authentication for eBGP and iBGP peers.
+4. Verify that for a BGP prefix next-hop information doesn't change
+ when same prefix is received from another peer via recursive lookup.
+5. Verify that BGP path attributes are present in CLI outputs and
+ JSON format, even if set to default.
+6. Verifying the BGP peering between loopback and physical link's IP
+ of 2 peer routers.
+7. Verify that BGP Active/Standby/Pre-emption/ECMP.
+"""
+
+import os
+import sys
+import time
+import pytest
+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, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ apply_raw_config,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ create_route_maps,
+ create_interface_in_kernel,
+ shutdown_bringup_interface,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_convergence_from_running_config,
+ modify_as_number,
+ verify_bgp_attributes,
+ clear_bgp,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Global variables
+BGP_CONVERGENCE = False
+KEEP_ALIVE_TIMER = 2
+HOLD_DOWN_TIMER = 6
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": ["100.1.1.1/32", "100.1.1.2/32"],
+ "ipv6": ["100::1/128", "100::2/128"],
+}
+
+RECUR_NEXT_HOP = {
+ "N1": {"ipv4": "20.20.20.20/24", "ipv6": "20:20::20:20/120"},
+ "N2": {"ipv4": "30.30.30.30/24", "ipv6": "30:30::30:30/120"},
+ "N3": {"ipv4": "40.40.40.40/24", "ipv6": "40:40::40:40/120"},
+}
+
+CHANGED_NEXT_HOP = {
+ "4thOctate": {"ipv4": "10.0.1.250/24", "ipv6": "fd00:0:0:1::100/64"},
+ "3rdOctate": {"ipv4": "10.0.10.2/24", "ipv6": "fd00:0:0:10::2/64"},
+}
+
+Loopabck_IP = {
+ "Lo_R1": {"ipv4": "1.1.1.1/32", "ipv6": "1:1::1:1/128"},
+ "Lo_R4": {"ipv4": "4.4.4.4/32", "ipv6": "4:4::4:4/128"},
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_recursive_route_ebgp_multi_hop.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global BGP_CONVERGENCE
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module : Failed \n Error : {}".format(
+ BGP_CONVERGENCE
+ )
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_recursive_routes_iBGP_peer_p1(request):
+ """
+ Verify that BGP routes are installed in iBGP peer, only
+ when there is a recursive route for next-hop reachability.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ dut = "r1"
+ protocol = "static"
+
+ step(
+ "Configure static routes on R1 pointing next-hop as connected"
+ "link between R1 & R3's IP"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r1"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_4)
+
+ step(
+ "Verify on router R1 that these static routes are "
+ "installed in RIB+FIB of R1"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute these static routes in BGP on router R1")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+
+ step(
+ "Verify on router R1 that these static routes are installed"
+ "in RIB table as well"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r1"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a static routes for next hop IP on R2 via multiple"
+ "recursive static routes"
+ )
+ dut = "r2"
+ create_interface_in_kernel(
+ tgen, dut, "lo", "40.40.40.50", netmask="255.255.255.0", create=True
+ )
+ create_interface_in_kernel(
+ tgen, dut, "lo", "40:40::40:50", netmask="120", create=True
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": topo["routers"]["r3"]["links"]["r1"][addr_type],
+ "next_hop": RECUR_NEXT_HOP["N1"][addr_type].split("/")[0],
+ },
+ {
+ "network": RECUR_NEXT_HOP["N1"][addr_type],
+ "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0],
+ },
+ {
+ "network": RECUR_NEXT_HOP["N2"][addr_type],
+ "next_hop": RECUR_NEXT_HOP["N3"][addr_type].split("/")[0],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("verify if redistributed routes are now installed in FIB of R2")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0],
+ protocol="bgp",
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Delete 1 route from static recursive for the next-hop IP")
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": RECUR_NEXT_HOP["N1"][addr_type],
+ "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Verify that redistributed routes are withdrawn from FIB of R2")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0],
+ protocol="bgp",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reconfigure the same static route on R2 again")
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": RECUR_NEXT_HOP["N1"][addr_type],
+ "next_hop": RECUR_NEXT_HOP["N2"][addr_type].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Verify that redistributed routes are again installed" "in FIB of R2")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0],
+ protocol="bgp",
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Configure static route with changed next-hop from same subnet")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r1"][
+ addr_type
+ ].split("/")[0],
+ "delete": True,
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[
+ 0
+ ],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that redistributed routes are not withdrawn as changed"
+ "next-hop IP, belongs to the same subnet"
+ )
+ result = verify_rib(tgen, addr_type, "r2", input_dict_4, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Configure static route with changed next-hop from different subnet")
+ dut = "r1"
+ create_interface_in_kernel(
+ tgen, dut, "lo10", "10.0.10.10", netmask="255.255.255.0", create=True
+ )
+ create_interface_in_kernel(
+ tgen, dut, "lo10", "fd00:0:0:10::104", netmask="64", create=True
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": CHANGED_NEXT_HOP["4thOctate"][addr_type].split("/")[
+ 0
+ ],
+ "delete": True,
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": CHANGED_NEXT_HOP["3rdOctate"][addr_type].split("/")[
+ 0
+ ],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_dict_4, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that redistributed routes are withdrawn as changed "
+ "next-hop IP, belongs to different subnet"
+ )
+ result = verify_rib(
+ tgen, addr_type, "r2", input_dict_4, protocol="bgp", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_next_hop_as_self_ip_p1(request):
+ """
+ Verify that any BGP prefix received with next hop as
+ self-ip is not installed in BGP RIB or FIB table.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure static routes on R1 with a next-hop IP belonging"
+ "to the same subnet of R2's link IP."
+ )
+ dut = "r1"
+ create_interface_in_kernel(
+ tgen,
+ dut,
+ "lo10",
+ topo["routers"]["r4"]["links"]["r2"]["ipv4"].split("/")[0],
+ netmask="255.255.255.0",
+ create=True,
+ )
+ create_interface_in_kernel(
+ tgen,
+ dut,
+ "lo10",
+ topo["routers"]["r4"]["links"]["r2"]["ipv6"].split("/")[0],
+ netmask="64",
+ create=True,
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r4"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_4)
+
+ step("Verify that static routes are installed in RIB and FIB of R1")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ protocol="static",
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static routes into BGP on R1")
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+
+ step(
+ "Verify that R2 denies the prefixes received in update message,"
+ "as next-hop IP belongs to connected interface"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r4"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut interface on R2 that has IP from the subnet as BGP next-hop")
+ intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_r4)
+
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r2")
+ step(
+ "Verify that redistributed routes now appear only in BGP table,"
+ "as next-hop IP is no more active on R2"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r4"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("No shutdown interface on R2 which was shut in previous step")
+ intf_r2_r4 = topo["routers"]["r2"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_r4, ifaceaction=True)
+
+ step(
+ "Verify that R2 dosn't install prefixes RIB to FIB as next-hop"
+ "interface is up now"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r2"]["links"]["r4"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r2",
+ input_dict_4,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_next_hop_with_recursive_lookup_p1(request):
+ """
+ Verify that for a BGP prefix next-hop information doesn't change
+ when same prefix is received from another peer via recursive lookup.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that BGP peering comes up.")
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Do redistribute connected on router R3.")
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify that R1 receives all connected")
+ for addr_type in ADDR_TYPES:
+ routes = {
+ "ipv4": ["1.0.3.17/32", "10.0.1.0/24", "10.0.3.0/24"],
+ "ipv6": ["2001:db8:f::3:17/128", "fd00:0:0:1::/64", "fd00:0:0:3::/64"],
+ }
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, "r1", input_dict, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a BGP neighborship between R1 and R4, directly via "
+ "eBGP multi-hop."
+ )
+ r1_local_as = topo["routers"]["r1"]["bgp"]["local_as"]
+ r1_r3_addr = topo["routers"]["r1"]["links"]["r3"]
+ r4_local_as = topo["routers"]["r4"]["bgp"]["local_as"]
+ r4_r3_addr = topo["routers"]["r4"]["links"]["r3"]
+ ebgp_multi_hop = 3
+
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(r1_local_as),
+ "neighbor {} remote-as {}".format(
+ r4_r3_addr[addr_type].split("/")[0], r4_local_as
+ ),
+ "neighbor {} timers {} {}".format(
+ r4_r3_addr[addr_type].split("/")[0],
+ KEEP_ALIVE_TIMER,
+ HOLD_DOWN_TIMER,
+ ),
+ "neighbor {} ebgp-multihop {}".format(
+ r4_r3_addr[addr_type].split("/")[0], ebgp_multi_hop
+ ),
+ ]
+ },
+ "r4": {
+ "raw_config": [
+ "router bgp {}".format(r4_local_as),
+ "neighbor {} remote-as {}".format(
+ r1_r3_addr[addr_type].split("/")[0], r1_local_as
+ ),
+ "neighbor {} timers {} {}".format(
+ r1_r3_addr[addr_type].split("/")[0],
+ KEEP_ALIVE_TIMER,
+ HOLD_DOWN_TIMER,
+ ),
+ "neighbor {} ebgp-multihop {}".format(
+ r1_r3_addr[addr_type].split("/")[0], ebgp_multi_hop
+ ),
+ ]
+ },
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ if addr_type == "ipv4":
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(r1_local_as),
+ "address-family {} unicast".format(addr_type),
+ "no neighbor {} activate".format(
+ r4_r3_addr["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ "r4": {
+ "raw_config": [
+ "router bgp {}".format(r4_local_as),
+ "address-family {} unicast".format(addr_type),
+ "no neighbor {} activate".format(
+ r1_r3_addr["ipv6"].split("/")[0]
+ ),
+ ]
+ },
+ }
+ else:
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(r1_local_as),
+ "address-family {} unicast".format(addr_type),
+ "neighbor {} activate".format(
+ r4_r3_addr[addr_type].split("/")[0]
+ ),
+ ]
+ },
+ "r4": {
+ "raw_config": [
+ "router bgp {}".format(r4_local_as),
+ "address-family {} unicast".format(addr_type),
+ "neighbor {} activate".format(
+ r1_r3_addr[addr_type].split("/")[0]
+ ),
+ ]
+ },
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that BGP session between R1 and R4 comes up" "(recursively via R3).")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Do redistribute connected on router R4.")
+ input_dict_1 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify that R1 now receives BGP prefix of link r3-r4 via 2 "
+ "next-hops R3 and R4. however do not install with NHT R4 in FIB."
+ )
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Clear bgp sessions from R1 using 'clear ip bgp *'")
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r1")
+
+ step(
+ "Verify that prefix of link r3-r4 is again learned via 2 "
+ "next-hops (from R3 and R4 directly)"
+ )
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribution from router R3.")
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify that peering between R1-R4 goes down and prefix "
+ "of link r3-r4, with NHT R4 is withdrawn."
+ )
+
+ logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER))
+ sleep(HOLD_DOWN_TIMER + 1)
+
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Re-apply redistribution on R3.")
+
+ input_dict_1 = {
+ "r3": {
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ "ipv6": {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify that peering between R1-R4 goes down and prefix "
+ "of link r3-r4 with NHT R4 is withdrawn."
+ )
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribution from router R4.")
+
+ input_dict_1 = {
+ "r4": {
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify that peering between R1-R4 doesn't go down but prefix "
+ "of link r3-r4 with NHT R4 is withdrawn."
+ )
+
+ logger.info("Sleeping for holddowntimer: {}".format(HOLD_DOWN_TIMER))
+ sleep(HOLD_DOWN_TIMER + 1)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r1",
+ input_dict,
+ protocol="bgp",
+ next_hop=next_hop,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Re-apply redistribution on R4.")
+
+ input_dict_1 = {
+ "r4": {
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "connected", "delete": True}
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify that prefix of link r3-r4 is re-learned via NHT R4.")
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Toggle the interface on R3.")
+
+ intf_r3_r4 = topo["routers"]["r3"]["links"]["r4"]["interface"]
+ shutdown_bringup_interface(tgen, "r3", intf_r3_r4)
+
+ step(
+ "Verify that peering between R1-R4 goes down and comes up when "
+ "interface is toggled. Also prefix of link r3-r4(via both NHTs) is"
+ " withdrawn and re-learned accordingly."
+ )
+
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r1",
+ input_dict,
+ protocol="bgp",
+ next_hop=next_hop,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format(
+ tc_name, result
+ )
+
+ shutdown_bringup_interface(tgen, "r3", intf_r3_r4, True)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Toggle the interface on R4.")
+
+ intf_r4_r3 = topo["routers"]["r4"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r3)
+
+ step(
+ "Verify that peering between R1-R4 goes down and comes up when"
+ "interface is toggled. Also prefix of link r3-r4(via R4)"
+ " is withdrawn and re-learned accordingly."
+ )
+
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "BGP is converged \n Error : {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r4"]["links"]["r3"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r1",
+ input_dict,
+ protocol="bgp",
+ next_hop=next_hop,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Route is still present \n Error : {}".format(
+ tc_name, result
+ )
+
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r3, True)
+
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ routes = {"ipv4": ["10.0.3.0/24"], "ipv6": ["fd00:0:0:3::/64"]}
+
+ input_dict = {"r1": {"static_routes": [{"network": routes[addr_type]}]}}
+ next_hop = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+
+ result = verify_rib(
+ tgen, addr_type, "r1", input_dict, protocol="bgp", next_hop=next_hop
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_path_attributes_default_values_p1(request):
+ """
+ Verify that BGP path attributes are present in CLI
+ outputs and JSON format, even if set to default.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config: Configure BGP neighborship, between R1-R2 & R1-R3")
+ reset_config_on_routers(tgen)
+
+ step("Advertise a set of prefixes from R1 to both peers R2 and R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+
+ step(
+ "Verify that advertised prefixes are received on R4 and well"
+ "known attributes are present in the CLI and JSON outputs with"
+ "default values without any route-map config."
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict_3,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r4": {
+ "route_maps": {
+ "rmap_pf": [{"set": {"origin": "incomplete", "aspath": "300 100"}}]
+ }
+ }
+ }
+
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ "r4",
+ NETWORK[addr_type],
+ rmap_name="rmap_pf",
+ input_dict=input_dict_4,
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map to set below attribute value as 500"
+ "and apply on R4 in an inbound direction"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r4": {
+ "route_maps": {
+ "Path_Attribue": [
+ {
+ "action": "permit",
+ "set": {
+ "path": {"as_num": 500, "as_action": "prepend"},
+ "locPrf": 500,
+ "origin": "egp",
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ input_dict_5 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify that once the route-map is applied all the attributes"
+ "part of route-map, changes value to 500"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r4": {
+ "route_maps": {
+ "rmap_pf": [
+ {
+ "set": {
+ "locPrf": 500,
+ "aspath": "500 300 100",
+ "origin": "EGP",
+ }
+ }
+ ]
+ }
+ }
+ }
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ "r4",
+ NETWORK[addr_type],
+ rmap_name="rmap_pf",
+ input_dict=input_dict_4,
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Remove the route-map from R4")
+ input_dict_5 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step(
+ "Verify on R4 that well known attributes are present in the CLI &"
+ "JSON outputs again with default values without route-map config"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r4": {
+ "route_maps": {
+ "rmap_pf": [{"set": {"aspath": "300 100", "origin": "incomplete"}}]
+ }
+ }
+ }
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ "r4",
+ NETWORK[addr_type],
+ rmap_name="rmap_pf",
+ input_dict=input_dict_4,
+ nexthop=None,
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_peering_bw_loopback_and_physical_p1(request):
+ """
+ Verifying the BGP peering between loopback and
+ physical link's IP of 2 peer routers.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step("Configure a loopback interface on R1")
+ dut = "r1"
+ create_interface_in_kernel(
+ tgen, dut, "lo10", "1.1.1.1", netmask="255.255.255.255", create=True
+ )
+ create_interface_in_kernel(
+ tgen, dut, "lo10", "1:1::1:1", netmask="128", create=True
+ )
+
+ step("Configure BGP session between R1's loopbak & R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": Loopabck_IP["Lo_R1"][addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_1,
+ protocol="static",
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]),
+ "address-family {} unicast".format(addr_type),
+ "neighbor {} update-source lo10".format(
+ topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+ ),
+ "neighbor {} timers 1 3".format(
+ topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+ ),
+ ]
+ },
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]),
+ "address-family {} unicast".format(addr_type),
+ "no neighbor {} remote-as {}".format(
+ topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
+ topo["routers"]["r1"]["bgp"]["local_as"],
+ ),
+ "neighbor {} remote-as {}".format(
+ Loopabck_IP["Lo_R1"][addr_type].split("/")[0],
+ topo["routers"]["r1"]["bgp"]["local_as"],
+ ),
+ "neighbor {} ebgp-multihop 3".format(
+ Loopabck_IP["Lo_R1"][addr_type].split("/")[0]
+ ),
+ ]
+ },
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ if addr_type == "ipv6":
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(
+ topo["routers"]["r3"]["bgp"]["local_as"]
+ ),
+ "address-family {} unicast".format(addr_type),
+ "neighbor {} activate".format(
+ Loopabck_IP["Lo_R1"][addr_type].split("/")[0]
+ ),
+ ]
+ }
+ }
+ else:
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(
+ topo["routers"]["r3"]["bgp"]["local_as"]
+ ),
+ "address-family {} unicast".format(addr_type),
+ "no neighbor {} activate".format(
+ Loopabck_IP["Lo_R1"]["ipv6"].split("/")[0]
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that BGP neighborship between R1 and R3 comes up")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove ebgp-multihop command from R3")
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]),
+ "no neighbor {} ebgp-multihop 3".format(
+ Loopabck_IP["Lo_R1"][addr_type].split("/")[0]
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that once eBGP multi-hop is removed, BGP session goes down")
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add ebgp-multihop command on R3 again")
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r3"]["bgp"]["local_as"]),
+ "neighbor {} ebgp-multihop 3".format(
+ Loopabck_IP["Lo_R1"][addr_type].split("/")[0]
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that BGP neighborship between R1 and R3 comes up")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove update-source command from R1")
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]),
+ "no neighbor {} update-source lo10".format(
+ topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that BGP session goes down, when update-source is removed")
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add update-source command on R1 again")
+ for addr_type in ADDR_TYPES:
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r1"]["bgp"]["local_as"]),
+ "neighbor {} update-source lo10".format(
+ topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error : {}".format(tc_name, result)
+
+ step("Verify that BGP neighborship between R1 and R3 comes up")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove static route from R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": Loopabck_IP["Lo_R1"][addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3"][
+ addr_type
+ ].split("/")[0],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_1,
+ protocol="static",
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ sleep(3)
+ step("Verify that BGP session goes down, when static route is removed")
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Add static route on R3 again")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": Loopabck_IP["Lo_R1"][addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r3",
+ input_dict_1,
+ protocol="static",
+ next_hop=topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0],
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Verify that BGP neighborship between R1 and R3 comes up")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Toggle physical interface on R1")
+ intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r3)
+ sleep(3)
+ step("Verify that BGP neighborship between R1 and R3 goes down")
+ result = verify_bgp_convergence_from_running_config(tgen, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ intf_r1_r3 = topo["routers"]["r1"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_r3, True)
+
+ step("Verify that BGP neighborship between R1 and R3 comes up")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BGP_active_standby_preemption_and_ecmp_p1(request):
+ """
+ Verify that BGP Active/Standby/Pre-emption/ECMP.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step("Change the AS number on R2 as 200")
+ input_dict = {"r2": {"bgp": {"local_as": 200}}}
+ result = modify_as_number(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify BGP converge after changing the AS number on R2")
+ result = verify_bgp_convergence_from_running_config(tgen)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Advertise a set of prefixes from R1 to both peers R2 & R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [{"network": NETWORK[addr_type], "next_hop": "null0"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r1": {
+ "bgp": {
+ "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 that R4 receives BGP prefixes via both peer routers R2 & R3")
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_bgp_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict_3,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ )
+ assert result is True, "Testcase {}: Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map to set as-path attribute and"
+ "apply on R3 in an inbound direction:"
+ )
+
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "Path_Attribue": [
+ {
+ "action": "permit",
+ "set": {"path": {"as_num": 123, "as_action": "prepend"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ input_dict_5 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify on R4, BGP routes with shorter as-path are installed in FIB")
+ for addr_type in ADDR_TYPES:
+ dut = "r4"
+ protocol = "bgp"
+ input_dict_6 = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_6,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Shutdown BGP neighorship between R1-R2")
+ dut = "r4"
+ intf_r4_r2 = topo["routers"]["r4"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf_r4_r2)
+
+ step(
+ "Verify that prefixes from next-hop via R2 are withdrawn"
+ "and installed via next-hop as R3"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_2,
+ next_hop=topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Do a no shut for BGP neighorship between R2-R4")
+ shutdown_bringup_interface(tgen, dut, intf_r4_r2, ifaceaction=True)
+
+ step(
+ "Verify that prefixes from next-hop via R3 are withdrawn"
+ "from R4 and installed via next-hop as R2 (preemption)"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_2,
+ next_hop=topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ protocol=protocol,
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove the route-map from R3's neighbor statement")
+ input_dict_5 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "Path_Attribue",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Configure multipath-relax and maximum-paths 2 on R4 for ECMP")
+ input_dict_8 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"maximum_paths": {"ebgp": 2}}},
+ "ipv6": {"unicast": {"maximum_paths": {"ebgp": 2}}},
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_8)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ maxpath_relax = {
+ "r4": {"bgp": {"local_as": "400", "bestpath": {"aspath": "multipath-relax"}}}
+ }
+
+ result = create_router_bgp(tgen, topo, maxpath_relax)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Remove multipath-relax command from R4")
+
+ del_maxpath_relax = {
+ "r4": {
+ "bgp": {
+ "local_as": "400",
+ "bestpath": {"aspath": "multipath-relax", "delete": True},
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, del_maxpath_relax)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify that ECMP is no longer happening on R4.")
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r4": {"static_routes": [{"network": NETWORK[addr_type]}]}}
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reconfigure multipath-relax command on R4")
+ result = create_router_bgp(tgen, topo, maxpath_relax)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove maximum-path 2 command from R4")
+ input_dict_8 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 1,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 1,
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_8)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify that ECMP is no longer happening on R4")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Re-configure maximum-path 2 command on R4")
+ input_dict_8 = {
+ "r4": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 2,
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "maximum_paths": {
+ "ebgp": 2,
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_8)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Verify FIB of R4, BGP prefixes with ECMP next-hop via R2 and R3")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ "r4",
+ input_dict,
+ next_hop=[
+ topo["routers"]["r2"]["links"]["r4"][addr_type].split("/")[0],
+ topo["routers"]["r3"]["links"]["r4"][addr_type].split("/")[0],
+ ],
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_password_authentication_for_eBGP_and_iBGP_peers_p1(request):
+ """
+ Verify password authentication for eBGP and iBGP peers.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Initial config :Configure BGP neighborship between R1 and R3.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Add a static route on R1 for loopbacks IP's reachability of R2, R3"
+ "and on R2 and R3 for loopback IP of R1"
+ )
+ for addr_type in ADDR_TYPES:
+ nh1 = topo["routers"]["r3"]["links"]["r1"][addr_type].split("/")[0]
+ nh2 = topo["routers"]["r1"]["links"]["r2"][addr_type].split("/")[0]
+ nh3 = topo["routers"]["r1"]["links"]["r3"][addr_type].split("/")[0]
+ nh4 = topo["routers"]["r2"]["links"]["r1"][addr_type].split("/")[0]
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": topo["routers"]["r3"]["links"]["lo"][addr_type],
+ "next_hop": nh1,
+ }
+ ]
+ }
+ }
+ input_dict_2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": topo["routers"]["r1"]["links"]["lo"][addr_type],
+ "next_hop": nh2,
+ }
+ ]
+ }
+ }
+ input_dict_3 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": topo["routers"]["r1"]["links"]["lo"][addr_type],
+ "next_hop": nh3,
+ }
+ ]
+ }
+ }
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": topo["routers"]["r2"]["links"]["lo"][addr_type],
+ "next_hop": nh4,
+ }
+ ]
+ }
+ }
+ dut_list = ["r1", "r2", "r3", "r1"]
+ nexthop_list = [nh1, nh2, nh3, nh4]
+ input_dict_list = [input_dict_1, input_dict_2, input_dict_3, input_dict_4]
+ for dut, next_hop, input_dict in zip(dut_list, nexthop_list, input_dict_list):
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Verify that static routes are installed in FIB of routers")
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, next_hop=next_hop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(
+ tc_name, result
+ )
+
+ step("Configure BGP sessions between R1-R2 and R1-R3 over loopback IPs")
+ for routerN in ["r1", "r3"]:
+ for addr_type in ADDR_TYPES:
+ if routerN == "r1":
+ bgp_neighbor = "r3"
+ elif routerN == "r3":
+ bgp_neighbor = "r1"
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {
+ "lo": {"ebgp_multihop": 2, "source_link": "lo"}
+ }
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ for routerN in ["r1", "r2"]:
+ for addr_type in ADDR_TYPES:
+ if routerN == "r1":
+ bgp_neighbor = "r2"
+ elif routerN == "r2":
+ bgp_neighbor = "r1"
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {"lo": {"source_link": "lo"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ for routerN in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ for bgp_neighbor in topo["routers"][routerN]["bgp"]["address_family"][
+ addr_type
+ ]["unicast"]["neighbor"].keys():
+ if routerN in ["r1", "r2", "r3"] and bgp_neighbor == "r4":
+ continue
+ if addr_type == "ipv4":
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][bgp_neighbor]["dest_link"] = {
+ "lo": {"deactivate": "ipv6"}
+ }
+ elif addr_type == "ipv6":
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][bgp_neighbor]["dest_link"] = {
+ "lo": {"deactivate": "ipv4"}
+ }
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Configure authentication password on R1 for neighbor statements")
+ for bgp_neighbor in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {"lo": {"password": "vmware"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step(
+ "Verify that both sessions go down as only R1 has password"
+ "configured but not peer routers"
+ )
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure same password on R2 and R3")
+ for routerN in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Verify that all BGP sessions come up due to identical passwords")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Configure same password on R2 and R3, but in CAPs.")
+ for routerN in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ]["r1"]["dest_link"] = {"lo": {"password": "VMWARE"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step(
+ "Verify that BGP sessions do not come up as password"
+ "strings are in CAPs on R2 and R3"
+ )
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure same password on R2 and R3 without CAPs")
+ for routerN in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ]["r1"]["dest_link"] = {"lo": {"password": "vmware"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Verify all BGP sessions come up again due to identical passwords")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error : {}".format(tc_name, result)
+
+ step("Remove password from R1")
+ for bgp_neighbor in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {"lo": {"no_password": "vmware"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Verify if password is removed from R1, both sessions go down again")
+ result = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "BGP is converged \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure alphanumeric password on R1 and peer routers R2,R3")
+ for bgp_neighbor in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"]["r1"]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ][bgp_neighbor]["dest_link"] = {"lo": {"password": "Vmware@123"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ for routerN in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ topo["routers"][routerN]["bgp"]["address_family"][addr_type]["unicast"][
+ "neighbor"
+ ]["r1"]["dest_link"] = {"lo": {"password": "Vmware@123"}}
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step(
+ "Verify that sessions Come up irrespective of characters"
+ "used in password string"
+ )
+ result = verify_bgp_convergence(tgen, topo)
+ 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/bgp_reject_as_sets/__init__.py b/tests/topotests/bgp_reject_as_sets/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/__init__.py
diff --git a/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf
new file mode 100644
index 0000000..a28b612
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r1/bgpd.conf
@@ -0,0 +1,10 @@
+! exit1
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_reject_as_sets/r1/zebra.conf b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf
new file mode 100644
index 0000000..9904bb4
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r1/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf
new file mode 100644
index 0000000..4539617
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r2/bgpd.conf
@@ -0,0 +1,13 @@
+! spine
+router bgp 65002
+ bgp reject-as-sets
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.254.2 timers 3 10
+ address-family ipv4 unicast
+ aggregate-address 172.16.0.0/16 as-set summary-only
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_reject_as_sets/r2/zebra.conf b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf
new file mode 100644
index 0000000..f0d357c
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r2/zebra.conf
@@ -0,0 +1,9 @@
+! spine
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+interface r2-eth1
+ ip address 192.168.254.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf
new file mode 100644
index 0000000..1dde4f0
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r3/bgpd.conf
@@ -0,0 +1,11 @@
+! exit2
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.254.1 remote-as 65002
+ neighbor 192.168.254.1 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.254.1 allowas-in
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_reject_as_sets/r3/zebra.conf b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf
new file mode 100644
index 0000000..f490d97
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/r3/zebra.conf
@@ -0,0 +1,9 @@
+! exit2
+interface lo
+ ip address 172.16.254.254/32
+!
+interface r3-eth0
+ ip address 192.168.254.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
new file mode 100644
index 0000000..8d1e834
--- /dev/null
+++ b/tests/topotests/bgp_reject_as_sets/test_bgp_reject_as_sets.py
@@ -0,0 +1,151 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_reject_as_sets.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if an aggregated route with AS_SET is not sent to peers.
+Addressing draft-ietf-idr-deprecate-as-set-confed-set recommendations.
+
+BGP speakers conforming to this document (i.e., conformant BGP
+ speakers) MUST NOT locally generate BGP UPDATE messages containing
+ AS_SET or AS_CONFED_SET. Conformant BGP speakers SHOULD NOT send BGP
+ UPDATE messages containing AS_SET or AS_CONFED_SET. Upon receipt of
+ such messages, conformant BGP speakers SHOULD use the "Treat-as-
+ withdraw" error handling behavior as per [RFC7606].
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_reject_as_sets():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_aggregated_route_with_stripped_as_set(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.0.0/16 json"))
+ expected = {
+ "paths": [{"aspath": {"string": "Local", "segments": [], "length": 0}}]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_announce_route_without_as_sets(router):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show ip bgp neighbor 192.168.254.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "172.16.0.0/16": {"path": ""},
+ "192.168.254.0/30": {"path": "65003"},
+ "192.168.255.0/30": {"path": "65001"},
+ },
+ "totalPrefixCounter": 3,
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(
+ _bgp_has_aggregated_route_with_stripped_as_set, router
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed to see an aggregated route in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_announce_route_without_as_sets, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Route 172.16.0.0/16 should be sent without AS_SET to r3 "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_remove_private_as/r1/bgpd.conf b/tests/topotests/bgp_remove_private_as/r1/bgpd.conf
new file mode 100644
index 0000000..9953689
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r1/bgpd.conf
@@ -0,0 +1,53 @@
+router bgp 65001
+ bgp router-id 192.0.2.1
+ no bgp network import-check
+ neighbor 203.0.113.1 remote-as 65002
+ neighbor 203.0.113.1 description r2
+ neighbor 203.0.113.1 timers 3 10
+ neighbor 203.0.113.3 remote-as 5555
+ neighbor 203.0.113.3 description r5
+ neighbor 203.0.113.3 timers 3 10
+!
+ address-family ipv4 unicast
+ network 100.64.0.0/32
+ network 100.64.0.1/32
+ network 100.64.0.2/32
+ network 100.64.0.3/32
+ network 100.64.0.4/32
+ network 100.64.0.5/32
+ neighbor 203.0.113.1 route-map set-as-paths out
+ neighbor 203.0.113.3 route-map set-as-paths out
+ exit-address-family
+!
+ip prefix-list match-0 seq 5 permit 100.64.0.0/32
+ip prefix-list match-1 seq 5 permit 100.64.0.1/32
+ip prefix-list match-2 seq 5 permit 100.64.0.2/32
+ip prefix-list match-3 seq 5 permit 100.64.0.3/32
+ip prefix-list match-4 seq 5 permit 100.64.0.4/32
+!
+! all private
+! at r3/r4, as-path should only have r2's asn
+route-map set-as-paths permit 10
+ match ip address prefix-list match-0
+ set as-path prepend 4200000000 4200000001 4200000002
+!
+! all private, include r3's asn
+! at r3/r4, as-path should only have r2's asn
+route-map set-as-paths permit 20
+ match ip address prefix-list match-1
+ set as-path prepend 65003 4200000000 4200000001 4200000002 65003
+!
+! mix of private/public
+route-map set-as-paths permit 30
+ match ip address prefix-list match-2
+ set as-path prepend 4200000000 1000 4200000001 2000 4200000002
+!
+! mix of private/public, include r3's asn multiple times
+route-map set-as-paths permit 40
+ match ip address prefix-list match-3
+ set as-path prepend 65003 4200000000 1000 4200000001 2000 4200000002 65003
+!
+! all public
+route-map set-as-paths permit 50
+ match ip address prefix-list match-4
+ set as-path prepend 1000 2000 2000 3000
diff --git a/tests/topotests/bgp_remove_private_as/r1/zebra.conf b/tests/topotests/bgp_remove_private_as/r1/zebra.conf
new file mode 100644
index 0000000..35c82d7
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r1/zebra.conf
@@ -0,0 +1,10 @@
+! to r2
+interface r1-eth0
+ ip address 203.0.113.0/31
+!
+! to r5
+interface r1-eth1
+ ip address 203.0.113.2/31
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_remove_private_as/r2/bgpd.conf b/tests/topotests/bgp_remove_private_as/r2/bgpd.conf
new file mode 100644
index 0000000..9655046
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r2/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 65002
+ bgp router-id 192.0.2.2
+ neighbor 203.0.113.0 remote-as 65001
+ neighbor 203.0.113.0 timers 3 10
+ neighbor 203.0.113.0 description r1
+ neighbor 203.0.113.4 remote-as 65003
+ neighbor 203.0.113.4 solo
+ neighbor 203.0.113.4 timers 3 10
+ neighbor 203.0.113.4 description r3
+ neighbor 203.0.113.8 remote-as 4444
+ neighbor 203.0.113.8 solo
+ neighbor 203.0.113.8 timers 3 10
+ neighbor 203.0.113.8 description r4
+!
+ address-family ipv4 unicast
+ neighbor 203.0.113.0 route-map permit-all in
+ neighbor 203.0.113.4 route-map permit-all out
+ neighbor 203.0.113.8 route-map permit-all out
+ exit-address-family
+!
+route-map permit-all permit 10
+!
diff --git a/tests/topotests/bgp_remove_private_as/r2/zebra.conf b/tests/topotests/bgp_remove_private_as/r2/zebra.conf
new file mode 100644
index 0000000..0168614
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r2/zebra.conf
@@ -0,0 +1,14 @@
+! to r1
+interface r2-eth0
+ ip address 203.0.113.1/31
+!
+! to r3
+interface r2-eth1
+ ip address 203.0.113.5/31
+!
+! to r4
+interface r2-eth2
+ ip address 203.0.113.9/31
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_remove_private_as/r3/bgpd.conf b/tests/topotests/bgp_remove_private_as/r3/bgpd.conf
new file mode 100644
index 0000000..0273d09
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r3/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 65003
+ bgp router-id 192.0.2.3
+ neighbor 203.0.113.5 remote-as 65002
+ neighbor 203.0.113.5 timers 3 10
+ neighbor 203.0.113.5 description r2
+ neighbor 203.0.113.7 remote-as 5555
+ neighbor 203.0.113.7 timers 3 10
+ neighbor 203.0.113.7 description r5
+!
+ address-family ipv4 unicast
+ neighbor 203.0.113.5 route-map permit-all in
+ neighbor 203.0.113.5 allowas-in 10
+ neighbor 203.0.113.5 soft-reconfiguration inbound
+ neighbor 203.0.113.7 route-map permit-all in
+ neighbor 203.0.113.7 allowas-in 10
+ neighbor 203.0.113.7 soft-reconfiguration inbound
+ exit-address-family
+!
+route-map permit-all permit 10
diff --git a/tests/topotests/bgp_remove_private_as/r3/zebra.conf b/tests/topotests/bgp_remove_private_as/r3/zebra.conf
new file mode 100644
index 0000000..e6a0ce3
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r3/zebra.conf
@@ -0,0 +1,10 @@
+! to r2
+interface r3-eth0
+ ip address 203.0.113.4/31
+!
+! to r5
+interface r3-eth1
+ ip address 203.0.113.6/31
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_remove_private_as/r4/bgpd.conf b/tests/topotests/bgp_remove_private_as/r4/bgpd.conf
new file mode 100644
index 0000000..f480105
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r4/bgpd.conf
@@ -0,0 +1,19 @@
+router bgp 4444
+ bgp router-id 192.0.2.4
+ neighbor 203.0.113.9 remote-as 65002
+ neighbor 203.0.113.9 timers 3 10
+ neighbor 203.0.113.9 description r2
+ neighbor 203.0.113.11 remote-as 5555
+ neighbor 203.0.113.11 timers 3 10
+ neighbor 203.0.113.11 description r5
+!
+ address-family ipv4 unicast
+ neighbor 203.0.113.9 route-map permit-all in
+ neighbor 203.0.113.9 allowas-in 10
+ neighbor 203.0.113.9 soft-reconfiguration inbound
+ neighbor 203.0.113.11 route-map permit-all in
+ neighbor 203.0.113.11 allowas-in 10
+ neighbor 203.0.113.11 soft-reconfiguration inbound
+ exit-address-family
+!
+route-map permit-all permit 10
diff --git a/tests/topotests/bgp_remove_private_as/r4/zebra.conf b/tests/topotests/bgp_remove_private_as/r4/zebra.conf
new file mode 100644
index 0000000..4de3300
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r4/zebra.conf
@@ -0,0 +1,10 @@
+! to r2
+interface r4-eth0
+ ip address 203.0.113.8/31
+!
+! to r5
+interface r4-eth1
+ ip address 203.0.113.10/31
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_remove_private_as/r5/bgpd.conf b/tests/topotests/bgp_remove_private_as/r5/bgpd.conf
new file mode 100644
index 0000000..67b4d43
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r5/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 5555
+ bgp router-id 192.0.2.5
+ neighbor 203.0.113.2 remote-as 65001
+ neighbor 203.0.113.2 timers 3 10
+ neighbor 203.0.113.2 description r1
+ neighbor 203.0.113.6 remote-as 65003
+ neighbor 203.0.113.6 solo
+ neighbor 203.0.113.6 timers 3 10
+ neighbor 203.0.113.6 description r3
+ neighbor 203.0.113.10 remote-as 4444
+ neighbor 203.0.113.10 solo
+ neighbor 203.0.113.10 timers 3 10
+ neighbor 203.0.113.10 description r4
+!
+ address-family ipv4 unicast
+ neighbor 203.0.113.2 route-map permit-all in
+ neighbor 203.0.113.6 route-map permit-all out
+ neighbor 203.0.113.10 route-map permit-all out
+ exit-address-family
+!
+route-map permit-all permit 10
+!
diff --git a/tests/topotests/bgp_remove_private_as/r5/zebra.conf b/tests/topotests/bgp_remove_private_as/r5/zebra.conf
new file mode 100644
index 0000000..f02daf4
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/r5/zebra.conf
@@ -0,0 +1,14 @@
+! to r1
+interface r5-eth0
+ ip address 203.0.113.3/31
+!
+! to r3
+interface r5-eth1
+ ip address 203.0.113.7/31
+!
+! to r4
+interface r5-eth2
+ ip address 203.0.113.11/31
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py
new file mode 100644
index 0000000..1108919
--- /dev/null
+++ b/tests/topotests/bgp_remove_private_as/test_bgp_remove_private_as.py
@@ -0,0 +1,430 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_remove_private_as.py
+#
+# Copyright (C) 2022 NVIDIA Corporation
+# Trey Aspelund
+#
+# 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 NVIDIA DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_remove_private_as.py tests the following conditions:
+1. "remove-private-AS" strips all private ASNs from the AS-path unless:
+ a. the ASN belongs to the peer
+ b. the ASN is both local + private
+ c. the AS-path is not completely comprised of public ASNs
+2. "remove-private-AS all" strips all private ASNs from the AS-path unless:
+ a. the ASN belongs to the peer
+ b. the ASN is both local + private
+3. "remove-private-AS replace-AS" swaps private ASNs with local ASN unless:
+ a. the ASN belongs to the peer
+ b. the AS-path is not completely comprised of public ASNs
+4. "remove-private-AS all replace-AS" swaps private ASNs with local ASN unless:
+ a. the ASN belongs to the peer
+
+All conditions are tested while the local ASN is private.
+All conditions are tested while the local ASN is public.
+All conditions are tested against an eBGP peer in a private ASN.
+All conditions are tested against an eBGP peer in a public ASN.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from functools import partial
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ """
+ We are effectively creating two hub/spoke topologies with r2 and r5 acting
+ as hubs. "remove-private-AS" will be configured on r2/r5 towards r3/r4, and
+ r1 will act as the originator of the test prefixes. AS-Path validation will
+ be done on r3/r4.
+
+ Topology:
+ +-----+ +-----+ +-----+
+ | r1 |----->|r2/r5|---->| r3 |
+ +-----+ +-----+ +-----+
+ |
+ v
+ +-----+
+ | r4 |
+ +-----+
+ ASNs:
+ - r1: 65001
+ - r2: 65002
+ - r3: 65003
+ - r4: 4444
+ - r5: 5555
+ """
+ for routern in range(1, 6):
+ tgen.add_router(f"r{routern}")
+
+ #######################
+ # Connections to r2
+ #######################
+ 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+ #######################
+ # Connections to r5
+ #######################
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r5"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r5"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r5"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, f"{rname}/zebra.conf")
+ )
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, f"{rname}/bgpd.conf"))
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_remove_private_as():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Test routes
+ prefixes = [
+ "100.64.0.0/32",
+ "100.64.0.1/32",
+ "100.64.0.2/32",
+ "100.64.0.3/32",
+ "100.64.0.4/32",
+ ]
+
+ # r2/r5 are setup with remove-private-AS configs.
+ tx_routers = ["r2", "r5"]
+
+ # We will validate the paths received by r3/r4.
+ rx_routers = ["r3", "r4"]
+
+ # Config options for remove-private-AS
+ remove_types = [
+ "remove-private-AS",
+ "remove-private-AS all",
+ "remove-private-AS replace-AS",
+ "remove-private-AS all replace-AS",
+ ]
+
+ # Expected as-paths for each test route from the perspective of each
+ # rx_router, accounting for each variation of remove-private-AS.
+ #
+ # Structure:
+ # expected_paths = {
+ # rx_router: {
+ # remove_type: {
+ # tx_router: {
+ # prefix: "path"
+ # }
+ # }
+ # }
+ # }
+ expected_paths = {
+ "r3": {
+ "remove-private-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002",
+ "100.64.0.1/32": "65002 65003 65003",
+ "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555",
+ "100.64.0.1/32": "5555 65003 65003",
+ "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS all": {
+ "r2": {
+ "100.64.0.0/32": "65002",
+ "100.64.0.1/32": "65002 65003 65003",
+ "100.64.0.2/32": "65002 1000 2000",
+ "100.64.0.3/32": "65002 65003 1000 2000 65003",
+ "100.64.0.4/32": "65002 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555",
+ "100.64.0.1/32": "5555 65003 65003",
+ "100.64.0.2/32": "5555 1000 2000",
+ "100.64.0.3/32": "5555 65003 1000 2000 65003",
+ "100.64.0.4/32": "5555 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS replace-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002 65002 65002 65002 65002",
+ "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003",
+ "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555 5555 5555 5555 5555",
+ "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003",
+ "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS all replace-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002 65002 65002 65002 65002",
+ "100.64.0.1/32": "65002 65002 65003 65002 65002 65002 65003",
+ "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002",
+ "100.64.0.3/32": "65002 65002 65003 65002 1000 65002 2000 65002 65003",
+ "100.64.0.4/32": "65002 65002 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555 5555 5555 5555 5555",
+ "100.64.0.1/32": "5555 5555 65003 5555 5555 5555 65003",
+ "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555",
+ "100.64.0.3/32": "5555 5555 65003 5555 1000 5555 2000 5555 65003",
+ "100.64.0.4/32": "5555 5555 1000 2000 2000 3000",
+ },
+ },
+ },
+ "r4": {
+ "remove-private-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002",
+ "100.64.0.1/32": "65002",
+ "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555",
+ "100.64.0.1/32": "5555",
+ "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS all": {
+ "r2": {
+ "100.64.0.0/32": "65002",
+ "100.64.0.1/32": "65002",
+ "100.64.0.2/32": "65002 1000 2000",
+ "100.64.0.3/32": "65002 1000 2000",
+ "100.64.0.4/32": "65002 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555",
+ "100.64.0.1/32": "5555",
+ "100.64.0.2/32": "5555 1000 2000",
+ "100.64.0.3/32": "5555 1000 2000",
+ "100.64.0.4/32": "5555 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS replace-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002 65002 65002 65002 65002",
+ "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002",
+ "100.64.0.2/32": "65002 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "65002 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "65002 65001 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555 5555 5555 5555 5555",
+ "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555",
+ "100.64.0.2/32": "5555 65001 4200000000 1000 4200000001 2000 4200000002",
+ "100.64.0.3/32": "5555 65001 65003 4200000000 1000 4200000001 2000 4200000002 65003",
+ "100.64.0.4/32": "5555 65001 1000 2000 2000 3000",
+ },
+ },
+ "remove-private-AS all replace-AS": {
+ "r2": {
+ "100.64.0.0/32": "65002 65002 65002 65002 65002",
+ "100.64.0.1/32": "65002 65002 65002 65002 65002 65002 65002",
+ "100.64.0.2/32": "65002 65002 65002 1000 65002 2000 65002",
+ "100.64.0.3/32": "65002 65002 65002 65002 1000 65002 2000 65002 65002",
+ "100.64.0.4/32": "65002 65002 1000 2000 2000 3000",
+ },
+ "r5": {
+ "100.64.0.0/32": "5555 5555 5555 5555 5555",
+ "100.64.0.1/32": "5555 5555 5555 5555 5555 5555 5555",
+ "100.64.0.2/32": "5555 5555 5555 1000 5555 2000 5555",
+ "100.64.0.3/32": "5555 5555 5555 5555 1000 5555 2000 5555 5555",
+ "100.64.0.4/32": "5555 5555 1000 2000 2000 3000",
+ },
+ },
+ },
+ }
+
+ # Simple lookup of remote peer ip by routers in session (local --> remote).
+ #
+ # Structure:
+ # peer_to_ip = {
+ # local_rtr: {
+ # peer_rtr: peer_ip
+ # }
+ # }
+ peer_to_ip = {
+ "r1": {"r2": "203.0.113.1", "r5": "203.0.113.3"},
+ "r2": {"r1": "203.0.113.0", "r3": "203.0.113.4", "r4": "203.0.113.8"},
+ "r3": {"r2": "203.0.113.5", "r5": "203.0.113.7"},
+ "r4": {"r2": "203.0.113.9", "r5": "203.0.113.11"},
+ "r5": {"r1": "203.0.113.2", "r3": "203.0.113.6", "r4": "203.0.113.10"},
+ }
+
+ def __bgp_up():
+ """Return True if all configured peers are Established."""
+ for router in tx_routers:
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp summary json")
+ )
+ numPeers = output["ipv4Unicast"]["totalPeers"]
+ numConverged = 0
+ for peer_data in output["ipv4Unicast"]["peers"].values():
+ if peer_data["state"] == "Established":
+ numConverged += 1
+ if numConverged == numPeers:
+ return True
+ return False
+
+ def __bgp_converged():
+ """Return True if all prefixes have been received from tx_routers."""
+ for router in rx_routers:
+ output = json.loads(
+ tgen.gears[router].vtysh_cmd("show ip bgp summary json")
+ )
+ numPeers = output["ipv4Unicast"]["totalPeers"]
+ numConverged = 0
+ for peer in tx_routers:
+ peer_ip = peer_to_ip[router][peer]
+ numPrefixes = output["ipv4Unicast"]["peers"][peer_ip]["pfxRcd"]
+ if numPrefixes == len(prefixes):
+ numConverged += 1
+ if numConverged == numPeers:
+ return True
+ return False
+
+ def _routers_up(tx_rtrs, rx_rtrs):
+ """Ensure all BGP sessions are up and all routes are installed."""
+ # all sessions go through tx_routers, so ensure all their peers are up
+ test_func = partial(__bgp_up)
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result == True, "Not all peers in Established state!"
+
+ # ensure correct number of routes are installed
+ test_func = partial(__bgp_converged)
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result == True, "Not all routes installed in time!"
+
+ def _change_remove_type(new_type, op):
+ """Update config with next remove-private-AS config variant."""
+ no = "no" if op == "del" else ""
+ for tr in tx_routers:
+ for rr in rx_routers:
+ p_ip = peer_to_ip[tr][rr]
+ tgen.gears[tr].vtysh_multicmd(
+ f"""
+ configure terminal
+ router bgp
+ address-family ipv4 unicast
+ {no} neighbor {p_ip} {new_type}
+ """
+ )
+
+ def _validate_paths(remove_type):
+ """Compare actual AS-Path against expected AS-Path."""
+ for rtr in rx_routers:
+ for peer in tx_routers:
+ p_ip = peer_to_ip[rtr][peer]
+ adj_rib_in = json.loads(
+ tgen.gears[rtr].vtysh_cmd(
+ f"show ip bgp neighbor {p_ip} received-routes json"
+ )
+ )
+ for pfx in prefixes:
+ good_path = expected_paths[rtr][remove_type][peer][pfx]
+ real_path = adj_rib_in["receivedRoutes"][pfx]["path"]
+ return real_path == good_path
+
+ #######################
+ # Begin Test
+ #######################
+
+ # make sure all peers come up and exchange routes
+ _routers_up(tx_routers, rx_routers)
+
+ # test each variation of remove-private-AS
+ for rmv_type in remove_types:
+ _change_remove_type(rmv_type, "add")
+
+ test_func = partial(_validate_paths, rmv_type)
+ _, result = topotest.run_and_expect(test_func, True, count=60, wait=0.5)
+ assert result == True, "Not all routes have correct AS-Path values!"
+
+ # each variation sets a separate peer flag in bgpd. we need to clear
+ # the old flag after each iteration so we only test the flags we expect.
+ _change_remove_type(rmv_type, "del")
+
+ return True
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/__init__.py b/tests/topotests/bgp_rfapi_basic_sanity/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/__init__.py
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/customize.py b/tests/topotests/bgp_rfapi_basic_sanity/customize.py
new file mode 100644
index 0000000..c82fe83
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/customize.py
@@ -0,0 +1,115 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017-2018 by
+# Network Device Education Foundation, Inc. ("NetDEF")
+# Modified by LabN Consulting, L.L.C.
+#
+# 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.
+#
+
+r"""
+customize.py: Simple FRR MPLS L3VPN test topology
+
+ +---------+
+ | r1 |
+ | 1.1.1.1 | PE Router
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 | P router
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 |
+ | 3.3.3.3 | | 4.4.4.4 | PE Routers
+ +-----------+ +---------+
+
+"""
+
+import os
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import get_topogen
+from lib.topolog import logger
+from lib.ltemplate import ltemplateRtrCmd
+
+# Required to instantiate the topology builder class.
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+# test name based on directory
+TEST = os.path.basename(CWD)
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ # Create P/PE routers
+ tgen.add_router("r1")
+ for routern in range(2, 5):
+ tgen.add_router("r{}".format(routern))
+ # Create a switch with just one router connected to it to simulate a
+ # empty network.
+ switch = {}
+ switch[0] = tgen.add_switch("sw0")
+ switch[0].add_link(tgen.gears["r1"], nodeif="r1-eth0")
+ switch[0].add_link(tgen.gears["r2"], nodeif="r2-eth0")
+
+ switch[1] = tgen.add_switch("sw1")
+ switch[1].add_link(tgen.gears["r2"], nodeif="r2-eth1")
+ switch[1].add_link(tgen.gears["r3"], nodeif="r3-eth0")
+ switch[1].add_link(tgen.gears["r4"], nodeif="r4-eth0")
+
+ switch[2] = tgen.add_switch("sw2")
+ switch[2].add_link(tgen.gears["r2"], nodeif="r2-eth2")
+ switch[2].add_link(tgen.gears["r3"], nodeif="r3-eth1")
+
+
+def ltemplatePreRouterStartHook():
+ cc = ltemplateRtrCmd()
+ tgen = get_topogen()
+ logger.info("pre router-start hook")
+ return True
+
+
+def ltemplatePostRouterStartHook():
+ logger.info("post router-start hook")
+ return True
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
new file mode 100644
index 0000000..f6e0baa
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/bgpd.conf
@@ -0,0 +1,50 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 1.1.1.1
+ bgp cluster-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 1.1.1.1
+!
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf
new file mode 100644
index 0000000..460a8fb
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf
new file mode 100644
index 0000000..18f61e0
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r1/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to sw0
+ ip address 10.0.1.1/24
+ no link-detect
+!
+interface r1-eth4
+ description to ce1
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.1/32 192.168.1.2
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
new file mode 100644
index 0000000..19050e6
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/bgpd.conf
@@ -0,0 +1,33 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 2.2.2.2
+ bgp cluster-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ neighbor 1.1.1.1 remote-as 5226
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 update-source 2.2.2.2
+ neighbor 3.3.3.3 remote-as 5226
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 update-source 2.2.2.2
+ neighbor 4.4.4.4 remote-as 5226
+ neighbor 4.4.4.4 timers 3 10
+ neighbor 4.4.4.4 update-source 2.2.2.2
+ address-family ipv4 unicast
+ no neighbor 1.1.1.1 activate
+ no neighbor 3.3.3.3 activate
+ no neighbor 4.4.4.4 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 1.1.1.1 activate
+ neighbor 1.1.1.1 route-reflector-client
+ neighbor 3.3.3.3 activate
+ neighbor 3.3.3.3 route-reflector-client
+ neighbor 4.4.4.4 activate
+ neighbor 4.4.4.4 route-reflector-client
+ exit-address-family
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf
new file mode 100644
index 0000000..dbed618
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf
new file mode 100644
index 0000000..dd1dbac
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r2/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ ip address 10.0.1.2/24
+ no link-detect
+!
+interface r2-eth1
+ description to sw1
+ ip address 10.0.2.2/24
+ no link-detect
+!
+interface r2-eth2
+ description to sw2
+ ip address 10.0.3.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
new file mode 100644
index 0000000..2210f24
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/bgpd.conf
@@ -0,0 +1,48 @@
+frr defaults traditional
+!
+hostname r3
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 3.3.3.3
+ bgp cluster-id 3.3.3.3
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 3.3.3.3
+!
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf
new file mode 100644
index 0000000..0e64ed6
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf
new file mode 100644
index 0000000..9dbc290
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r3/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r3
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ ip address 10.0.2.3/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+ no link-detect
+!
+interface r3-eth4
+ description to ce2
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.2/32 192.168.1.2
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
new file mode 100644
index 0000000..28b5f9c
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/bgpd.conf
@@ -0,0 +1,49 @@
+frr defaults traditional
+!
+hostname r4
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 4.4.4.4
+ bgp cluster-id 4.4.4.4
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 4.4.4.4
+!
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf
new file mode 100644
index 0000000..89e37df
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/4 area 0
+ redistribute static
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf
new file mode 100644
index 0000000..415f03d
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/r4/zebra.conf
@@ -0,0 +1,23 @@
+log file zebra.log
+!
+hostname r4
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ ip address 10.0.2.4/24
+ no link-detect
+!
+interface r4-eth4
+ description to ce3
+ ip address 192.168.1.1/24
+ no link-detect
+!
+ip route 99.0.0.3/32 192.168.1.2
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py
new file mode 100644
index 0000000..bc47dfc
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/add_routes.py
@@ -0,0 +1,159 @@
+from lib.lutil import luCommand
+
+holddownFactorSet = luCommand(
+ "r1",
+ 'vtysh -c "show running"',
+ "rfp holddown-factor",
+ "none",
+ "Holddown factor set",
+)
+if not holddownFactorSet:
+ to = "-1"
+ cost = ""
+else:
+ to = "6"
+ cost = "cost 50"
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev open vn 10.0.0.1 un 1.1.1.1"',
+ "rfapi_set_response_cb: status 0",
+ "pass",
+ "Opened RFAPI",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"',
+ "rc=2",
+ "pass",
+ "Clean query",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24 lifetime {}"'.format(
+ to
+ ),
+ "",
+ "none",
+ "Prefix registered",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "1 out of 1",
+ "wait",
+ "Local registration",
+)
+luCommand("r1", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none")
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 11.11.11.11"',
+ "11.11.11.0/24",
+ "pass",
+ "Query self",
+)
+
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev open vn 10.0.0.2 un 2.2.2.2"',
+ "rfapi_set_response_cb: status 0",
+ "pass",
+ "Opened RFAPI",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24 lifetime {}"'.format(
+ to
+ ),
+ "",
+ "none",
+ "Prefix registered",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "1 out of 1",
+ "wait",
+ "Local registration",
+)
+luCommand("r3", 'vtysh -c "debug rfapi-dev response-omit-self on"', ".", "none")
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 22.22.22.22"',
+ "rc=2",
+ "pass",
+ "Self excluded",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev open vn 10.0.1.2 un 2.1.1.2"',
+ "rfapi_set_response_cb: status 0",
+ "pass",
+ "Opened query only RFAPI",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.1.2 un 2.1.1.2 target 22.22.22.22"',
+ "22.22.22.0/24",
+ "pass",
+ "See local",
+)
+
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev open vn 10.0.0.3 un 3.3.3.3"',
+ "rfapi_set_response_cb: status 0",
+ "pass",
+ "Opened RFAPI",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24 lifetime {}"'.format(
+ to
+ ),
+ "",
+ "none",
+ "Prefix registered",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "1 out of 1",
+ "wait",
+ "Local registration",
+)
+luCommand("r4", 'vtysh -c "debug rfapi-dev response-omit-self off"', ".", "none")
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 33.33.33.33"',
+ "33.33.33.0/24",
+ "pass",
+ "Query self",
+)
+
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24 lifetime {} {}"'.format(
+ to, cost
+ ),
+ "",
+ "none",
+ "MP Prefix registered",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "2 out of 2",
+ "wait",
+ "Local registration",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"',
+ "11.11.11.0/24",
+ "pass",
+ "Query self MP",
+)
+
+luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py
new file mode 100644
index 0000000..9878cdc
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/adjacencies.py
@@ -0,0 +1,50 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r3", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r4", "ping 2.2.2.2 -c 1", " 0. packet loss", "wait", "PE->P2 (loopback) ping", 60
+)
+luCommand(
+ "r2",
+ 'vtysh -c "show bgp summary"',
+ " 00:0.* 00:0.* 00:0",
+ "wait",
+ "Core adjacencies up",
+ 180,
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0",
+ "wait",
+ "All adjacencies up",
+ 180,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0",
+ "wait",
+ "All adjacencies up",
+ 180,
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show bgp vrf all summary"',
+ " 00:0",
+ "wait",
+ "All adjacencies up",
+ 180,
+)
+luCommand(
+ "r1", "ping 3.3.3.3 -c 1", " 0. packet loss", "wait", "PE->PE3 (loopback) ping"
+)
+luCommand(
+ "r1", "ping 4.4.4.4 -c 1", " 0. packet loss", "wait", "PE->PE4 (loopback) ping"
+)
+# luCommand('r4','ping 3.3.3.3 -c 1',' 0. packet loss','wait','PE->PE3 (loopback) ping')
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py
new file mode 100644
index 0000000..e68fac8
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_close.py
@@ -0,0 +1,102 @@
+from lib.lutil import luCommand
+
+holddownFactorSet = luCommand(
+ "r1",
+ 'vtysh -c "show running"',
+ "rfp holddown-factor",
+ "none",
+ "Holddown factor set",
+)
+if not holddownFactorSet:
+ to = "-1"
+else:
+ to = "1"
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev open vn 20.0.0.1 un 1.1.1.21"',
+ "rfapi_set_response_cb: status 0",
+ "pass",
+ "Opened RFAPI",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev register vn 20.0.0.1 un 1.1.1.21 prefix 111.111.111.0/24 lifetime {}"'.format(
+ to
+ ),
+ "",
+ "none",
+ "Prefix registered",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "111.111.111.0/24",
+ "wait",
+ "Local registration",
+ 1,
+)
+luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "111.111.111.0/24",
+ "wait",
+ "See registration",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "111.111.111.0/24",
+ "wait",
+ "See registration",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev close vn 20.0.0.1 un 1.1.1.21"',
+ "status 0",
+ "pass",
+ "Closed RFAPI",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3",
+ "wait",
+ "See cleanup",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3",
+ "wait",
+ "See cleanup",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 2",
+ "wait",
+ "See cleanup",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ 20,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+)
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py
new file mode 100644
index 0000000..24b3cba
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_routes.py
@@ -0,0 +1,74 @@
+from lib.lutil import luCommand
+
+luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI")
+luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI")
+luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI")
+luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', "", "none", "VPN SAFI")
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3",
+ "wait",
+ "See all registrations",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3",
+ "wait",
+ "See all registrations",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 2",
+ "wait",
+ "See all registrations",
+)
+num = "4 routes and 4"
+luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay")
+luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay")
+luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay")
+luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI okay")
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 22.22.22.22"',
+ "pfx=",
+ "pass",
+ "Query R2s info",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 33.33.33.33"',
+ "pfx=",
+ "pass",
+ "Query R4s info",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 11.11.11.11"',
+ "11.11.11.0/24.*11.11.11.0/24.*",
+ "pass",
+ "Query R1s+R4s info",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.2 un 2.2.2.2 target 33.33.33.33"',
+ "pfx=",
+ "pass",
+ "Query R4s info",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 11.11.11.11"',
+ "11.11.11.0/24.*11.11.11.0/24.*",
+ "pass",
+ "Query R1s+R4s info",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 22.22.22.22"',
+ "pfx=",
+ "pass",
+ "Query R2s info",
+)
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py
new file mode 100644
index 0000000..f5c2db2
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/check_timeout.py
@@ -0,0 +1,325 @@
+from lib.lutil import luCommand
+
+holddownFactorSet = luCommand(
+ "r1",
+ 'vtysh -c "show running"',
+ "rfp holddown-factor",
+ "none",
+ "Holddown factor set",
+)
+luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
+if not holddownFactorSet:
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc summary"',
+ ".",
+ "pass",
+ "Holddown factor not set -- skipping test",
+ )
+else:
+ # holddown time test
+ luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"',
+ "",
+ "none",
+ "Prefix registered",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "1.111.0.0/16",
+ "wait",
+ "Local registration",
+ )
+
+ luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"',
+ "",
+ "none",
+ "Prefix registered",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "1.222.0.0/16",
+ "wait",
+ "Local registration",
+ )
+
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Remotely: *Active: 4 ",
+ "wait",
+ "See registrations, L=10",
+ )
+
+ luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16 lifetime 5 cost 50"',
+ "",
+ "none",
+ "MP Prefix registered",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations local"',
+ "1.222.0.0/16",
+ "wait",
+ "Local registration (MP prefix)",
+ )
+
+ luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+ luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+
+ luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.111.111.111"',
+ "pfx=",
+ "pass",
+ "Query R1s info",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.3 un 3.3.3.3 target 1.222.222.222"',
+ "1.222.0.0/16.*1.222.0.0/16",
+ "pass",
+ "Query R3s+R4s info",
+ )
+
+ luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 1.222.0.0/16"',
+ "",
+ "none",
+ "MP Prefix removed",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 1 ",
+ "wait",
+ "MP prefix in holddown",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 1 ",
+ "wait",
+ "MP prefix in holddown",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 1 ",
+ "wait",
+ "MP prefix in holddown",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev query vn 10.0.0.1 un 1.1.1.1 target 1.222.222.222"',
+ "1.222.0.0/16",
+ "pass",
+ "Query R3s info",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16"',
+ "",
+ "none",
+ "Prefix timeout",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations holddown"',
+ "1.111.0.0/16",
+ "wait",
+ "Local holddown",
+ 1,
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16"',
+ "",
+ "none",
+ "Prefix timeout",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations holddown"',
+ "1.222.0.0/16",
+ "wait",
+ "Local holddown",
+ 1,
+ )
+ luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
+ luCommand("r4", 'vtysh -c "show vnc registrations"', ".", "none")
+
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 2 ",
+ "wait",
+ "In holddown",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 2 ",
+ "wait",
+ "In holddown",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 2 ",
+ "wait",
+ "In holddown",
+ )
+
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ 20,
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ )
+
+ # kill test
+ luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 lifetime 10"',
+ "",
+ "none",
+ "Prefix registered",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations local"',
+ "1.111.0.0/16",
+ "wait",
+ "Local registration",
+ )
+
+ luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev register vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 lifetime 10"',
+ "",
+ "none",
+ "Prefix registered",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations local"',
+ "1.222.0.0/16",
+ "wait",
+ "Local registration",
+ )
+
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Remotely: *Active: 4 ",
+ "wait",
+ "See registrations L=10 (pre-kill)",
+ 5,
+ )
+ luCommand("r1", 'vtysh -c "show vnc registrations"', ".", "none")
+ luCommand("r3", 'vtysh -c "show vnc registrations"', ".", "none")
+ luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 1.111.0.0/16 kill"',
+ "",
+ "none",
+ "Prefix kill",
+ )
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 4 .*In Holddown: *Active: 0",
+ "wait",
+ "Registration killed",
+ 1,
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1",
+ "wait",
+ "Remote in holddown",
+ 5,
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 3 .*In Holddown: *Active: 1",
+ "wait",
+ "Remote in holddown",
+ 5,
+ )
+
+ luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 1.222.0.0/16 kill"',
+ "",
+ "none",
+ "Prefix kill",
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 1",
+ "wait",
+ "Registration killed",
+ 1,
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 2",
+ "wait",
+ "Remote in holddown",
+ 5,
+ )
+
+ luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ 20,
+ )
+ luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 1 .* Remotely: *Active: 3 .*In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ )
+ luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 2 .* Remotely: *Active: 2 .*In Holddown: *Active: 0",
+ "wait",
+ "Out of holddown",
+ )
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
new file mode 100644
index 0000000..7201ac8
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/scripts/cleanup_all.py
@@ -0,0 +1,124 @@
+from lib.lutil import luCommand
+
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.1 un 1.1.1.1 prefix 11.11.11.0/24"',
+ "",
+ "none",
+ "Prefix removed",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 ",
+ "wait",
+ "Local registration removed",
+)
+luCommand(
+ "r1",
+ 'vtysh -c "debug rfapi-dev close vn 10.0.0.1 un 1.1.1.1"',
+ "status 0",
+ "pass",
+ "Closed RFAPI",
+)
+
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.2 un 2.2.2.2 prefix 22.22.22.0/24"',
+ "",
+ "none",
+ "Prefix removed",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 ",
+ "wait",
+ "Local registration removed",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "debug rfapi-dev close vn 10.0.0.2 un 2.2.2.2"',
+ "status 0",
+ "pass",
+ "Closed RFAPI",
+)
+
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 33.33.33.0/24"',
+ "",
+ "none",
+ "Prefix removed",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "debug rfapi-dev unregister vn 10.0.0.3 un 3.3.3.3 prefix 11.11.11.0/24"',
+ "",
+ "none",
+ "MP prefix removed",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 ",
+ "wait",
+ "Local registration removed",
+)
+# luCommand('r4','vtysh -c "debug rfapi-dev close vn 10.0.0.3 un 3.3.3.3"','status 0', 'pass', 'Closed RFAPI')
+luCommand("r4", 'vtysh -c "clear vnc nve *"', ".", "pass", "Cleared NVEs")
+
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0",
+ "wait",
+ "All registrations cleared",
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0",
+ "wait",
+ "All registrations cleared",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0",
+ "wait",
+ "All registrations cleared",
+)
+
+num = "0 exist"
+luCommand("r1", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear")
+luCommand("r2", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear")
+luCommand("r3", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear")
+luCommand("r4", 'vtysh -c "show bgp ipv4 vpn"', num, "pass", "VPN SAFI clear")
+
+luCommand(
+ "r1",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0",
+ "wait",
+ "No holddowns",
+ 20,
+)
+luCommand(
+ "r3",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0",
+ "wait",
+ "No holddowns",
+)
+luCommand(
+ "r4",
+ 'vtysh -c "show vnc registrations"',
+ "Locally: *Active: 0 .* Remotely: *Active: 0 .*In Holddown: *Active: 0",
+ "wait",
+ "No holddowns",
+)
+
+luCommand("r1", 'vtysh -c "show vnc summary"', ".", "none")
+luCommand("r3", 'vtysh -c "show vnc summary"', ".", "none")
+luCommand("r4", 'vtysh -c "show vnc summary"', ".", "none")
diff --git a/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py
new file mode 100755
index 0000000..fa04aaf
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import sys
+import pytest
+
+sys.path.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), ".."))
+
+from lib.ltemplate import *
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd]
+
+
+def test_add_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/add_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_adjacencies():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/adjacencies.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_routes():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/check_routes.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_close():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/check_close.py", False, CliOnFail, CheckFunc)
+
+
+def test_check_timeout():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/check_timeout.py", False, CliOnFail, CheckFunc)
+
+
+def test_cleanup_all():
+ CliOnFail = None
+ # For debugging, uncomment the next line
+ # CliOnFail = 'tgen.mininet_cli'
+ CheckFunc = "ltemplateVersionCheck('3.1')"
+ # uncomment next line to start cli *before* script is run
+ # CheckFunc = 'ltemplateVersionCheck(\'3.1\', cli=True)'
+ ltemplateTest("scripts/cleanup_all.py", False, CliOnFail, CheckFunc)
+
+
+if __name__ == "__main__":
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/__init__.py
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py
new file mode 120000
index 0000000..a6b653a
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/customize.py
@@ -0,0 +1 @@
+../bgp_rfapi_basic_sanity/customize.py \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
new file mode 100644
index 0000000..3196a16
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/bgpd.conf
@@ -0,0 +1,51 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 1.1.1.1
+ bgp cluster-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 1.1.1.1
+!
+ address-family ipv4 unicast
+ redistribute vnc-direct
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+ rfp full-table-download off
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf
new file mode 120000
index 0000000..d09b09e
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/ospfd.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r1/ospfd.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf
new file mode 120000
index 0000000..728b1b9
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r1/zebra.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r1/zebra.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
new file mode 100644
index 0000000..19050e6
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/bgpd.conf
@@ -0,0 +1,33 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 2.2.2.2
+ bgp cluster-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ neighbor 1.1.1.1 remote-as 5226
+ neighbor 1.1.1.1 timers 3 10
+ neighbor 1.1.1.1 update-source 2.2.2.2
+ neighbor 3.3.3.3 remote-as 5226
+ neighbor 3.3.3.3 timers 3 10
+ neighbor 3.3.3.3 update-source 2.2.2.2
+ neighbor 4.4.4.4 remote-as 5226
+ neighbor 4.4.4.4 timers 3 10
+ neighbor 4.4.4.4 update-source 2.2.2.2
+ address-family ipv4 unicast
+ no neighbor 1.1.1.1 activate
+ no neighbor 3.3.3.3 activate
+ no neighbor 4.4.4.4 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 1.1.1.1 activate
+ neighbor 1.1.1.1 route-reflector-client
+ neighbor 3.3.3.3 activate
+ neighbor 3.3.3.3 route-reflector-client
+ neighbor 4.4.4.4 activate
+ neighbor 4.4.4.4 route-reflector-client
+ exit-address-family
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf
new file mode 120000
index 0000000..834554e
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/ospfd.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r2/ospfd.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf
new file mode 120000
index 0000000..2fefda5
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r2/zebra.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r2/zebra.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
new file mode 100644
index 0000000..e74fc0b
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/bgpd.conf
@@ -0,0 +1,49 @@
+frr defaults traditional
+!
+hostname r3
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 3.3.3.3
+ bgp cluster-id 3.3.3.3
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 3.3.3.3
+!
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+ rfp full-table-download off
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf
new file mode 120000
index 0000000..353b9ad
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/ospfd.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r3/ospfd.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf
new file mode 120000
index 0000000..44a63cf
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r3/zebra.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r3/zebra.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
new file mode 100644
index 0000000..56474aa
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/bgpd.conf
@@ -0,0 +1,50 @@
+frr defaults traditional
+!
+hostname r4
+password zebra
+log stdout notifications
+log commands
+router bgp 5226
+ bgp router-id 4.4.4.4
+ bgp cluster-id 4.4.4.4
+ no bgp ebgp-requires-policy
+ neighbor 2.2.2.2 remote-as 5226
+ neighbor 2.2.2.2 timers 3 10
+ neighbor 2.2.2.2 update-source 4.4.4.4
+!
+ address-family ipv4 unicast
+ no neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ address-family ipv4 vpn
+ neighbor 2.2.2.2 activate
+ exit-address-family
+!
+ rfp holddown-factor 0
+ rfp full-table-download off
+!
+ vnc defaults
+ rd auto:vn:123
+ response-lifetime 45
+ rt both 1000:1 1000:2
+ exit-vnc
+!
+ vnc nve-group red
+ prefix vn 10.0.0.0/8
+ rd auto:vn:10
+ rt both 1000:10
+ exit-vnc
+!
+ vnc nve-group blue
+ prefix vn 20.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:20
+ exit-vnc
+!
+ vnc nve-group green
+ prefix vn 30.0.0.0/8
+ rd auto:vn:20
+ rt both 1000:30
+ exit-vnc
+!
+end
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf
new file mode 120000
index 0000000..3d7a0aa
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/ospfd.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r4/ospfd.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf
new file mode 120000
index 0000000..c2fcd19
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/r4/zebra.conf
@@ -0,0 +1 @@
+../../bgp_rfapi_basic_sanity/r4/zebra.conf \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts b/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts
new file mode 120000
index 0000000..8ac974d
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/scripts
@@ -0,0 +1 @@
+../bgp_rfapi_basic_sanity/scripts/ \ No newline at end of file
diff --git a/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py b/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py
new file mode 120000
index 0000000..54a67a8
--- /dev/null
+++ b/tests/topotests/bgp_rfapi_basic_sanity_config2/test_bgp_rfapi_basic_sanity_config2.py
@@ -0,0 +1 @@
+../bgp_rfapi_basic_sanity/test_bgp_rfapi_basic_sanity.py \ No newline at end of file
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/__init__.py b/tests/topotests/bgp_rmap_extcommunity_none/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/__init__.py
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf b/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf
new file mode 100644
index 0000000..8ccdf9c
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/r1/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+!
+route-map r2 permit 10
+ set extcommunity none
+!
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf b/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf b/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf
new file mode 100644
index 0000000..9d58078
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/r2/bgpd.conf
@@ -0,0 +1,12 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.1 route-map r1 out
+ exit-address-family
+!
+route-map r1 permit 10
+ set community 123:123
+ set extcommunity bandwidth 200
+!
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf b/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf
new file mode 100644
index 0000000..dc15cf7
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ip address 172.16.16.1/32
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py b/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py
new file mode 100644
index 0000000..d34ac3c
--- /dev/null
+++ b/tests/topotests/bgp_rmap_extcommunity_none/test_bgp_rmap_extcommunity_none.py
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if route-map extcommunity none works:
+
+route-map <name> permit 10
+ set extcommunity none
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_extcommunity_none():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")
+ )
+ expected = {
+ "prefix": "172.16.16.1/32",
+ "paths": [
+ {
+ "community": {
+ "string": "123:123",
+ },
+ "extendedCommunity": {"string": "LB:65002:25000000 (200.000 Mbps)"},
+ }
+ ],
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "BGP Converge failed"
+
+ def _bgp_extcommunity_strip(router):
+ router.vtysh_cmd(
+ "conf t\nrouter bgp 65001\naddress-family ipv4\nneighbor 192.168.1.2 route-map r2 in"
+ )
+ output = json.loads(
+ router.vtysh_cmd("show bgp ipv4 unicast 172.16.16.1/32 json")
+ )
+ expected = {
+ "prefix": "172.16.16.1/32",
+ "paths": [
+ {
+ "community": {
+ "string": "123:123",
+ },
+ "extendedCommunity": None,
+ }
+ ],
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_extcommunity_strip, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Failed to strip incoming extended communities from r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_roles_capability/__init__.py b/tests/topotests/bgp_roles_capability/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/__init__.py
diff --git a/tests/topotests/bgp_roles_capability/r1/bgpd.conf b/tests/topotests/bgp_roles_capability/r1/bgpd.conf
new file mode 100644
index 0000000..273f933
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r1/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 64501
+ bgp router-id 192.168.2.1
+ network 192.0.2.0/24
+! Correct role pair
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 local-role provider
+ neighbor 192.168.2.2 timers 3 10
+! Incorrect role pair
+ neighbor 192.168.3.2 remote-as external
+ neighbor 192.168.3.2 local-role provider
+ neighbor 192.168.3.2 timers 3 10
+! Missed neighbor role
+ neighbor 192.168.4.2 remote-as external
+ neighbor 192.168.4.2 local-role provider
+ neighbor 192.168.4.2 timers 3 10
+! Missed neighbor role with strict-mode
+ neighbor 192.168.5.2 remote-as external
+ neighbor 192.168.5.2 local-role provider strict-mode
+ neighbor 192.168.5.2 timers 3 10
+! Testing peer-groups
+ neighbor PG peer-group
+ neighbor PG remote-as external
+ neighbor PG local-role provider
+ neighbor PG timers 3 10
+ neighbor 192.168.6.2 peer-group PG
diff --git a/tests/topotests/bgp_roles_capability/r1/zebra.conf b/tests/topotests/bgp_roles_capability/r1/zebra.conf
new file mode 100644
index 0000000..301a1e8
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r1/zebra.conf
@@ -0,0 +1,18 @@
+!
+interface r1-eth0
+ ip address 192.168.2.1/24
+!
+interface r1-eth1
+ ip address 192.168.3.1/24
+!
+interface r1-eth2
+ ip address 192.168.4.1/24
+!
+interface r1-eth3
+ ip address 192.168.5.1/24
+!
+interface r1-eth4
+ ip address 192.168.6.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r2/bgpd.conf b/tests/topotests/bgp_roles_capability/r2/bgpd.conf
new file mode 100644
index 0000000..aa8ba72
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r2/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 64502
+ bgp router-id 192.168.2.2
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 local-role customer
+ neighbor 192.168.2.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_capability/r2/zebra.conf b/tests/topotests/bgp_roles_capability/r2/zebra.conf
new file mode 100644
index 0000000..86a9784
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r3/bgpd.conf b/tests/topotests/bgp_roles_capability/r3/bgpd.conf
new file mode 100644
index 0000000..5ed93d6
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r3/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 64503
+ neighbor 192.168.3.1 remote-as external
+ neighbor 192.168.3.1 local-role peer
+ neighbor 192.168.3.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_capability/r3/zebra.conf b/tests/topotests/bgp_roles_capability/r3/zebra.conf
new file mode 100644
index 0000000..9df578e
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.3.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r4/bgpd.conf b/tests/topotests/bgp_roles_capability/r4/bgpd.conf
new file mode 100644
index 0000000..118132d
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r4/bgpd.conf
@@ -0,0 +1,3 @@
+router bgp 64504
+ neighbor 192.168.4.1 remote-as external
+ neighbor 192.168.4.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_capability/r4/zebra.conf b/tests/topotests/bgp_roles_capability/r4/zebra.conf
new file mode 100644
index 0000000..03632ee
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.4.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r5/bgpd.conf b/tests/topotests/bgp_roles_capability/r5/bgpd.conf
new file mode 100644
index 0000000..2f62fbf
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r5/bgpd.conf
@@ -0,0 +1,3 @@
+router bgp 64505
+ neighbor 192.168.5.1 remote-as external
+ neighbor 192.168.5.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_capability/r5/zebra.conf b/tests/topotests/bgp_roles_capability/r5/zebra.conf
new file mode 100644
index 0000000..2e95d77
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r5/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r5-eth0
+ ip address 192.168.5.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/r6/bgpd.conf b/tests/topotests/bgp_roles_capability/r6/bgpd.conf
new file mode 100644
index 0000000..44b12fe
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r6/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 64506
+ neighbor 192.168.6.1 remote-as external
+ neighbor 192.168.6.1 local-role customer
+ neighbor 192.168.6.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_capability/r6/zebra.conf b/tests/topotests/bgp_roles_capability/r6/zebra.conf
new file mode 100644
index 0000000..c8428c4
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/r6/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r6-eth0
+ ip address 192.168.6.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.dot b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot
new file mode 100644
index 0000000..c0b5c81
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/roles_capability_stand.dot
@@ -0,0 +1,15 @@
+graph roles_filtering_stand {
+ layout="circo"
+ label="roles capability stand"
+ fontsize="20"
+
+ r1 [label="r1 provider"];
+ r2 [label="r2"];
+ r3 [label="r3"];
+ r4 [label="r4"];
+ r5 [label="r5"];
+ r1 -- r2 [headlabel="customer", taillabel="provider"];
+ r1 -- r3 [headlabel="peer", taillabel="provider"];
+ r1 -- r4 [headlabel="?", taillabel="provider"];
+ r1 -- r5 [headlabel="?", taillabel="provider", label="strict-mode"];
+}
diff --git a/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
new file mode 100644
index 0000000..b8dea2f
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/roles_capability_stand.jpg
Binary files differ
diff --git a/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py
new file mode 100644
index 0000000..d72fd19
--- /dev/null
+++ b/tests/topotests/bgp_roles_capability/test_bgp_roles_capability.py
@@ -0,0 +1,185 @@
+#!/usr/bin/python
+#
+# test_bgp_roles_capability.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Eugene Bogomazov <eb@qrator.net>
+# Copyright (c) 2017 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 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.
+#
+
+"""
+test_bgp_roles_capability: test bgp roles negotiation
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+topodef = {f"s{i}": ("r1", f"r{i}") for i in range(2, 7)}
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_BGP, "bgpd.conf")
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def find_neighbor_status(router, neighbor_ip):
+ return json.loads(router.vtysh_cmd(f"show bgp neighbors {neighbor_ip} json"))[
+ neighbor_ip
+ ]
+
+
+def check_role_mismatch(router, neighbor_ip):
+ return is_role_mismatch(find_neighbor_status(router, neighbor_ip))
+
+
+def is_role_mismatch(neighbor_status):
+ return (
+ neighbor_status["bgpState"] != "Established"
+ and neighbor_status.get("lastErrorCodeSubcode") == "020B" # <2, 11>
+ and "Role Mismatch" in neighbor_status.get("lastNotificationReason", "")
+ )
+
+
+def check_session_established(router, neighbor_ip):
+ neighbor_status = find_neighbor_status(router, neighbor_ip)
+ return neighbor_status["bgpState"] == "Established"
+
+
+def test_correct_pair(tgen):
+ # provider-customer pair
+ router = tgen.gears["r1"]
+ neighbor_ip = "192.168.2.2"
+ check_r2_established = functools.partial(
+ check_session_established, router, neighbor_ip
+ )
+ success, result = topotest.run_and_expect(
+ check_r2_established, True, count=20, wait=3
+ )
+ assert success, "Session with r2 is not Established"
+
+ neighbor_status = find_neighbor_status(router, neighbor_ip)
+ assert neighbor_status["localRole"] == "provider"
+ assert neighbor_status["remoteRole"] == "customer"
+ assert (
+ neighbor_status["neighborCapabilities"].get("role") == "advertisedAndReceived"
+ )
+
+
+def test_role_pair_mismatch(tgen):
+ # provider-peer mistmatch
+ router = tgen.gears["r3"]
+ neighbor_ip = "192.168.3.1"
+ check_r3_mismatch = functools.partial(check_role_mismatch, router, neighbor_ip)
+ success, result = topotest.run_and_expect(check_r3_mismatch, True, count=20, wait=3)
+ assert success, "Session between r1 and r3 was not correctly closed"
+
+
+def test_single_role_advertising(tgen):
+ # provider-undefined pair; we set role
+ router = tgen.gears["r1"]
+ neighbor_ip = "192.168.4.2"
+ check_r4_established = functools.partial(
+ check_session_established, router, neighbor_ip
+ )
+ success, result = topotest.run_and_expect(
+ check_r4_established, True, count=20, wait=3
+ )
+ assert success, "Session with r4 is not Established"
+
+ neighbor_status = find_neighbor_status(router, neighbor_ip)
+ assert neighbor_status["localRole"] == "provider"
+ assert neighbor_status["remoteRole"] == "undefined"
+ assert neighbor_status["neighborCapabilities"].get("role") == "advertised"
+
+
+def test_single_role_receiving(tgen):
+ # provider-undefined pair; we receive role
+ router = tgen.gears["r4"]
+ neighbor_ip = "192.168.4.1"
+ check_r1_established = functools.partial(
+ check_session_established, router, neighbor_ip
+ )
+ success, result = topotest.run_and_expect(
+ check_r1_established, True, count=20, wait=3
+ )
+ assert success, "Session with r1 is not Established"
+
+ neighbor_status = find_neighbor_status(router, neighbor_ip)
+ assert neighbor_status["localRole"] == "undefined"
+ assert neighbor_status["remoteRole"] == "provider"
+ assert neighbor_status["neighborCapabilities"].get("role") == "received"
+
+
+def test_role_strict_mode(tgen):
+ # provider-undefined pair with strict-mode
+ router = tgen.gears["r5"]
+ neighbor_ip = "192.168.5.1"
+ check_r5_mismatch = functools.partial(check_role_mismatch, router, neighbor_ip)
+ success, result = topotest.run_and_expect(check_r5_mismatch, True, count=20, wait=3)
+ assert success, "Session between r1 and r5 was not correctly closed"
+
+
+def test_correct_pair_peer_group(tgen):
+ # provider-customer pair (using peer-groups)
+ router = tgen.gears["r1"]
+ neighbor_ip = "192.168.6.2"
+ check_r6_established = functools.partial(
+ check_session_established, router, neighbor_ip
+ )
+ success, _ = topotest.run_and_expect(check_r6_established, True, count=20, wait=3)
+ assert success, "Session with r6 is not Established"
+
+ neighbor_status = find_neighbor_status(router, neighbor_ip)
+ assert neighbor_status["localRole"] == "provider"
+ assert neighbor_status["remoteRole"] == "customer"
+ assert (
+ neighbor_status["neighborCapabilities"].get("role") == "advertisedAndReceived"
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_roles_filtering/__init__.py b/tests/topotests/bgp_roles_filtering/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/__init__.py
diff --git a/tests/topotests/bgp_roles_filtering/r1/bgpd.conf b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf
new file mode 100644
index 0000000..99f6211
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r1/bgpd.conf
@@ -0,0 +1,12 @@
+! Provider on this side
+router bgp 64501
+ bgp router-id 192.168.1.1
+ no bgp network import-check
+ network 192.0.2.1/32
+ neighbor 192.168.1.2 remote-as 64510
+ neighbor 192.168.1.2 local-role provider
+ neighbor 192.168.1.2 route-map ALLOW_ALL out
+ neighbor 192.168.1.2 route-map ALLOW_ALL in
+ neighbor 192.168.1.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r1/zebra.conf b/tests/topotests/bgp_roles_filtering/r1/zebra.conf
new file mode 100644
index 0000000..acf120b
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r10/bgpd.conf b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf
new file mode 100644
index 0000000..f60bc6e
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r10/bgpd.conf
@@ -0,0 +1,21 @@
+! Customer on other side
+router bgp 64510
+ bgp router-id 192.168.10.1
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 64501
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.2.1 remote-as 64502
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.3.1 remote-as 64503
+ neighbor 192.168.3.1 timers 3 10
+ neighbor 192.168.4.1 remote-as 64504
+ neighbor 192.168.4.1 timers 3 10
+ neighbor 192.168.5.1 remote-as 64505
+ neighbor 192.168.5.1 local-role provider
+ neighbor 192.168.5.1 timers 3 10
+ neighbor 192.168.6.1 remote-as 64506
+ neighbor 192.168.6.1 local-role peer
+ neighbor 192.168.6.1 timers 3 10
+ neighbor 192.168.7.1 remote-as 64507
+ neighbor 192.168.7.1 local-role customer
+ neighbor 192.168.7.1 timers 3 10
diff --git a/tests/topotests/bgp_roles_filtering/r10/zebra.conf b/tests/topotests/bgp_roles_filtering/r10/zebra.conf
new file mode 100644
index 0000000..f2733fe
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r10/zebra.conf
@@ -0,0 +1,24 @@
+!
+interface r10-eth0
+ ip address 192.168.1.2/24
+!
+interface r10-eth1
+ ip address 192.168.2.2/24
+!
+interface r10-eth2
+ ip address 192.168.3.2/24
+!
+interface r10-eth3
+ ip address 192.168.4.2/24
+!
+interface r10-eth4
+ ip address 192.168.5.2/24
+!
+interface r10-eth5
+ ip address 192.168.6.2/24
+!
+interface r10-eth6
+ ip address 192.168.7.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r2/bgpd.conf b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf
new file mode 100644
index 0000000..b6db8c1
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r2/bgpd.conf
@@ -0,0 +1,12 @@
+! With peer on this side
+router bgp 64502
+ bgp router-id 192.168.2.1
+ no bgp network import-check
+ network 192.0.2.2/32
+ neighbor 192.168.2.2 remote-as 64510
+ neighbor 192.168.2.2 local-role peer
+ neighbor 192.168.2.2 route-map ALLOW_ALL out
+ neighbor 192.168.2.2 route-map ALLOW_ALL in
+ neighbor 192.168.2.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r2/zebra.conf b/tests/topotests/bgp_roles_filtering/r2/zebra.conf
new file mode 100644
index 0000000..f785ea1
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r3/bgpd.conf b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf
new file mode 100644
index 0000000..70f10b1
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r3/bgpd.conf
@@ -0,0 +1,12 @@
+! Customer role on this side
+router bgp 64503
+ bgp router-id 192.168.3.1
+ no bgp network import-check
+ network 192.0.2.3/32
+ neighbor 192.168.3.2 remote-as 64510
+ neighbor 192.168.3.2 local-role customer
+ neighbor 192.168.3.2 route-map ALLOW_ALL out
+ neighbor 192.168.3.2 route-map ALLOW_ALL in
+ neighbor 192.168.3.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r3/zebra.conf b/tests/topotests/bgp_roles_filtering/r3/zebra.conf
new file mode 100644
index 0000000..b347257
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 192.168.3.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r4/bgpd.conf b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf
new file mode 100644
index 0000000..11e324e
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r4/bgpd.conf
@@ -0,0 +1,11 @@
+! Without role on this side
+router bgp 64504
+ bgp router-id 192.168.4.1
+ no bgp network import-check
+ network 192.0.2.4/32
+ neighbor 192.168.4.2 remote-as 64510
+ neighbor 192.168.4.2 route-map ALLOW_ALL out
+ neighbor 192.168.4.2 route-map ALLOW_ALL in
+ neighbor 192.168.4.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r4/zebra.conf b/tests/topotests/bgp_roles_filtering/r4/zebra.conf
new file mode 100644
index 0000000..3543c08
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 192.168.4.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r5/bgpd.conf b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf
new file mode 100644
index 0000000..39d2a8d
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r5/bgpd.conf
@@ -0,0 +1,11 @@
+! Provider on other side
+router bgp 64505
+ bgp router-id 192.168.5.1
+ no bgp network import-check
+ network 192.0.2.5/32
+ neighbor 192.168.5.2 remote-as 64510
+ neighbor 192.168.5.2 route-map ALLOW_ALL out
+ neighbor 192.168.5.2 route-map ALLOW_ALL in
+ neighbor 192.168.5.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r5/zebra.conf b/tests/topotests/bgp_roles_filtering/r5/zebra.conf
new file mode 100644
index 0000000..4a1c273
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r5/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r5-eth0
+ ip address 192.168.5.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r6/bgpd.conf b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf
new file mode 100644
index 0000000..25e5cd8
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r6/bgpd.conf
@@ -0,0 +1,11 @@
+! Peer on other side
+router bgp 64506
+ bgp router-id 192.168.6.1
+ no bgp network import-check
+ network 192.0.2.6/32
+ neighbor 192.168.6.2 remote-as 64510
+ neighbor 192.168.6.2 route-map ALLOW_ALL out
+ neighbor 192.168.6.2 route-map ALLOW_ALL in
+ neighbor 192.168.6.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r6/zebra.conf b/tests/topotests/bgp_roles_filtering/r6/zebra.conf
new file mode 100644
index 0000000..3644a69
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r6/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r6-eth0
+ ip address 192.168.6.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/r7/bgpd.conf b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf
new file mode 100644
index 0000000..5f5f257
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r7/bgpd.conf
@@ -0,0 +1,11 @@
+! Customer on other side
+router bgp 64507
+ bgp router-id 192.168.7.1
+ no bgp network import-check
+ network 192.0.2.7/32
+ neighbor 192.168.7.2 remote-as 64510
+ neighbor 192.168.7.2 route-map ALLOW_ALL out
+ neighbor 192.168.7.2 route-map ALLOW_ALL in
+ neighbor 192.168.7.2 timers 3 10
+
+route-map ALLOW_ALL permit 999
diff --git a/tests/topotests/bgp_roles_filtering/r7/zebra.conf b/tests/topotests/bgp_roles_filtering/r7/zebra.conf
new file mode 100644
index 0000000..0407a48
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/r7/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r7-eth0
+ ip address 192.168.7.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot
new file mode 100644
index 0000000..df0f685
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.dot
@@ -0,0 +1,21 @@
+graph roles_filtering_stand {
+ layout="circo"
+ label="roles filtering stand"
+ fontsize="20"
+
+ r1 [label="r1 192.0.2.1/32"];
+ r2 [label="r2 192.0.2.2/32"];
+ r3 [label="r3 192.0.2.3/32"];
+ r4 [label="r4 192.0.2.4/32"];
+ r5 [label="r5 192.0.2.5/32"];
+ r6 [label="r6 192.0.2.6/32"];
+ r7 [label="r7 192.0.2.7/32"];
+ r10 [label="r10 intermediate"];
+ r10 -- r1 [headlabel="provider", taillabel="?"];
+ r10 -- r2 [headlabel="peer", taillabel="?"];
+ r10 -- r3 [headlabel="customer", taillabel="?"];
+ r10 -- r4 [headlabel="?", taillabel="?"];
+ r10 -- r5 [headlabel="?", taillabel="provider"];
+ r10 -- r6 [headlabel="?", taillabel="peer"];
+ r10 -- r7 [headlabel="?", taillabel="customer"];
+}
diff --git a/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
new file mode 100644
index 0000000..dfedcf8
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/roles_filtering_stand.jpg
Binary files differ
diff --git a/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
new file mode 100644
index 0000000..c5827d7
--- /dev/null
+++ b/tests/topotests/bgp_roles_filtering/test_bgp_roles_filtering.py
@@ -0,0 +1,144 @@
+#!/usr/bin/python
+#
+# test_bgp_roles_filtering.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Eugene Bogomazov <eb@qrator.net>
+# Copyright (c) 2017 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 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.
+#
+
+"""
+test_bgp_roles_filtering: test leaks prevention and mitigation with roles
+"""
+
+import json
+import os
+import sys
+import functools
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.bgp import verify_bgp_convergence_from_running_config
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+topodef = {f"s{i}": (f"r{i}", "r10") for i in range(1, 8)}
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_BGP, "bgpd.conf")
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def test_r10_routes(tgen):
+ # provider-undefine pair bur strict-mode was set
+ def _routes_half_converged():
+ routes = json.loads(tgen.gears["r10"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ output = sorted(routes.keys())
+ expected = [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+ return output == expected
+
+ success, result = topotest.run_and_expect(
+ _routes_half_converged, True, count=20, wait=3
+ )
+ assert success, "Routes did not converged"
+
+ routes_with_otc = list()
+ for number in range(1, 8):
+ prefix = f"192.0.2.{number}/32"
+ route_details = json.loads(
+ tgen.gears["r10"].vtysh_cmd(f"show bgp ipv4 {prefix} json")
+ )
+ if route_details["paths"][0].get("otc") is not None:
+ routes_with_otc.append(prefix)
+ assert routes_with_otc == [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+
+
+def test_r1_routes(tgen):
+ routes = json.loads(tgen.gears["r1"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.1/32", # own
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ ]
+
+
+def test_r6_routes(tgen):
+ routes = json.loads(tgen.gears["r6"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32", # own
+ ]
+
+
+def test_r4_routes(tgen):
+ routes = json.loads(tgen.gears["r4"].vtysh_cmd("show bgp ipv4 json"))["routes"]
+ routes_list = sorted(routes.keys())
+ assert routes_list == [
+ "192.0.2.1/32",
+ "192.0.2.2/32",
+ "192.0.2.3/32",
+ "192.0.2.4/32",
+ "192.0.2.5/32",
+ "192.0.2.6/32",
+ "192.0.2.7/32",
+ ]
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_aggregation/bgp_aggregation.json b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json
new file mode 100644
index 0000000..5520f67
--- /dev/null
+++ b/tests/topotests/bgp_route_aggregation/bgp_aggregation.json
@@ -0,0 +1,249 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "192.168.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "192.168.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4": {
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4": {
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
new file mode 100644
index 0000000..8c9d2c9
--- /dev/null
+++ b/tests/topotests/bgp_route_aggregation/test_bgp_aggregation.py
@@ -0,0 +1,1173 @@
+#!/usr/bin/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 bgp aggregation functionality:
+
+1. Verify route summarisation with summary-only for redistributed as well as
+ locally generated routes.
+2. Verify route summarisation with as-set for redistributed routes.
+
+"""
+
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ create_route_maps,
+ create_prefix_lists,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_community,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+
+NETWORK_1_1 = {"ipv4": "10.1.1.0/24", "ipv6": "10:1::1:0/120"}
+NETWORK_1_2 = {"ipv4": "10.1.2.0/24", "ipv6": "10:1::2:0/120"}
+NETWORK_1_3 = {"ipv4": "10.1.3.0/24", "ipv6": "10:1::3:0/120"}
+NETWORK_1_4 = {"ipv4": "10.1.4.0/24", "ipv6": "10:1::4:0/120"}
+NETWORK_1_5 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"}
+NETWORK_2_1 = {"ipv4": "10.1.1.100/32", "ipv6": "10:1::1:0/124"}
+NETWORK_2_2 = {"ipv4": "10.1.5.0/24", "ipv6": "10:1::5:0/120"}
+NETWORK_2_3 = {"ipv4": "10.1.6.0/24", "ipv6": "10:1::6:0/120"}
+NETWORK_2_4 = {"ipv4": "10.1.7.0/24", "ipv6": "10:1::7:0/120"}
+NETWORK_3_1 = {"ipv4": "10.1.8.0/24", "ipv6": "10:1::8:0/120"}
+NETWORK_4_1 = {"ipv4": "10.2.1.0/24", "ipv6": "10:2::1:0/120"}
+NEXT_HOP = {"ipv4": "Null0", "ipv6": "Null0"}
+AGGREGATE_NW = {"ipv4": "10.1.0.0/20", "ipv6": "10:1::/96"}
+
+COMMUNITY = [
+ "0:1 0:10 0:100",
+ "0:2 0:20 0:200",
+ "0:3 0:30 0:300",
+ "0:4 0:40 0:400",
+ "0:5 0:50 0:500",
+ "0:1 0:2 0:3 0:4 0:5 0:10 0:20 0:30 0:40 0:50 0:100 0:200 0:300 0:400 0:500",
+ "0:3 0:4 0:5 0:30 0:40 0:50 0:300 0:400 0:500",
+ "0:6 0:60 0:600",
+ "0:7 0:70 0:700",
+ "0:3 0:4 0:5 0:6 0:30 0:40 0:50 0:60 0:300 0:400 0:500 0:600",
+]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_aggregation.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ ADDR_TYPES = check_address_types()
+
+ for addr_type in ADDR_TYPES:
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error:" " {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_route_summarisation_with_summary_only_p1(request):
+ """
+ Verify route summarisation with summary-only for redistributed as well as
+ locally generated routes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ reset_config_on_routers(tgen)
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static routes on router R1 and redistribute in " "BGP process.")
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK_1_1[addr_type],
+ NETWORK_1_2[addr_type],
+ NETWORK_1_3[addr_type],
+ ],
+ "next_hop": NEXT_HOP[addr_type],
+ }
+ ]
+ }
+ }
+ input_redistribute = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ step("Configuring {} static routes on router R1 ".format(addr_type))
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configuring redistribute static for {} address-family on router R1 ".format(
+ addr_type
+ )
+ )
+
+ result = create_router_bgp(tgen, topo, input_redistribute)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that Static routes are redistributed in BGP process")
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK_1_1[addr_type],
+ NETWORK_1_2[addr_type],
+ NETWORK_1_3[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Advertise some prefixes using network command")
+ step(
+ "Additionally advertise 10.1.4.0/24 & 10.1.5.0/24 and "
+ "10:1::4:0/120 & 10:1::5:0/120 from R4 to R1."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_2_1[addr_type],
+ NETWORK_2_2[addr_type],
+ NETWORK_2_3[addr_type],
+ NETWORK_2_4[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_1_4[addr_type],
+ NETWORK_1_5[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that advertised prefixes using network command are being "
+ "advertised in BGP process"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_2_1[addr_type],
+ NETWORK_2_2[addr_type],
+ NETWORK_2_3[addr_type],
+ NETWORK_2_4[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", input_advertise)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure aggregate-address to summarise all the advertised routes.")
+
+ for addr_type in ADDR_TYPES:
+ route_aggregate = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": AGGREGATE_NW[addr_type],
+ "summary": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, route_aggregate)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that we see 1 summarised route and remaining suppressed "
+ "routes on advertising router R1 and only 1 summarised route on "
+ "receiving router R3 for both AFIs."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_agg = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK_1_1[addr_type],
+ NETWORK_1_2[addr_type],
+ NETWORK_1_3[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", input_static_agg, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r3", input_static, protocol="bgp", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static_agg, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["removed", "add"], [True, False]):
+
+ step(
+ "{} static routes as below: "
+ "(no) ip route 10.1.1.0/24 and (no) ip route 10.1.2.0/24"
+ "(no) ipv6 route 10:1::1:0/120 and (no) ip route 10:1::2:0/120".format(
+ action
+ )
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]],
+ "next_hop": NEXT_HOP[addr_type],
+ "delete": value,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that there is no impact on R3, as summarised route remains "
+ "intact. However suppressed routes on R1 disappear and re-appear "
+ "based on {} static routes.".format(action)
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]]}
+ ]
+ }
+ }
+
+ input_static_2 = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+
+ if value:
+ result = verify_rib(
+ tgen, addr_type, "r1", input_static_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "r1", input_static_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", input_static_2, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "{} prefixes using network command as below:"
+ "(no) network 10.1.6.1/24 and (no) network 10.1.7.1/24"
+ "(no) network 10:1::6:0/120 and (no) network 10:1::7:0/120".format(action)
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_2_3[addr_type],
+ NETWORK_2_4[addr_type],
+ ],
+ "delete": value,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that there is no impact on R3, as summarised route remains "
+ "intact. However suppressed routes on R1 disappear and re-appear "
+ "based on {} of network command.".format(action)
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_advertise_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_2_3[addr_type],
+ NETWORK_2_4[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ input_advertise_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {"network": AGGREGATE_NW[addr_type]}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_advertise_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes are still present \n Error: {}".format(tc_name, result)
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_advertise_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", input_advertise_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Add a new network each one from out of aggregation range and "
+ "other within aggregation range. "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK_3_1[addr_type], "next_hop": NEXT_HOP[addr_type]}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_advertise = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": NETWORK_4_1[addr_type],
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_advertise)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when a network within aggregation range is added, "
+ "there is no impact on receiving router. However if a network "
+ "outside aggregation range is added/removed, R3 receives and "
+ "withdraws it accordingly."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static = {"r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}}
+
+ result = verify_rib(tgen, addr_type, "r3", input_static, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_advertise_2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": [
+ NETWORK_4_1[addr_type],
+ AGGREGATE_NW[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", input_advertise_2, protocol="bgp")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Delete", "Re-add"], [True, False]):
+ step("{} aggregation command from R1.".format(action))
+
+ for addr_type in ADDR_TYPES:
+ route_aggregate = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": AGGREGATE_NW[addr_type],
+ "summary": True,
+ "delete": value,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, route_aggregate)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on both routers that summarised route is withdrawn from R1 "
+ "and R3 when aggregate-address command is removed and appears again "
+ "when aggregate-address command is re-added. Check for both AFIs."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_agg = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+
+ if value:
+ result = verify_rib(
+ tgen, addr_type, "r1", input_static_agg, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r3", input_static_agg, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "r1", input_static_agg)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", input_static_agg)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_route_summarisation_with_as_set_p1(request):
+ """
+ Verify route summarisation with as-set for redistributed routes.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ reset_config_on_routers(tgen)
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure static routes on router R1 and redistribute in " "BGP process.")
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK_1_1[addr_type],
+ NETWORK_1_2[addr_type],
+ NETWORK_1_3[addr_type],
+ NETWORK_1_4[addr_type],
+ NETWORK_1_5[addr_type],
+ ],
+ "next_hop": NEXT_HOP[addr_type],
+ }
+ ]
+ }
+ }
+ input_redistribute = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ step("Configuring {} static routes on router R1 ".format(addr_type))
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configuring redistribute static for {} address-family on router R1 ".format(
+ addr_type
+ )
+ )
+
+ result = create_router_bgp(tgen, topo, input_redistribute)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that Static routes are redistributed in BGP process")
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK_1_1[addr_type],
+ NETWORK_1_2[addr_type],
+ NETWORK_1_3[addr_type],
+ NETWORK_1_4[addr_type],
+ NETWORK_1_5[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map to attach a unique community attribute value "
+ "to each of these prefixes, while re-distributing static."
+ )
+
+ for addr_type in ADDR_TYPES:
+ for pfx, seq_id, network, in zip(
+ [1, 2, 3, 4, 5],
+ [10, 20, 30, 40, 50],
+ [NETWORK_1_1, NETWORK_1_2, NETWORK_1_3, NETWORK_1_4, NETWORK_1_5],
+ ):
+ prefix_list = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_{}_{}".format(addr_type, pfx): [
+ {
+ "seqid": seq_id,
+ "network": network[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, prefix_list)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-map for applying prefix-list on r1")
+
+ for addr_type in ADDR_TYPES:
+ for pfx, comm_id in zip([1, 2, 3, 4, 5], [0, 1, 2, 3, 4]):
+ route_map = {
+ "r1": {
+ "route_maps": {
+ "rmap_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_{}_{}".format(
+ addr_type, pfx
+ )
+ }
+ },
+ "set": {"community": {"num": COMMUNITY[comm_id]}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Re-configure redistribute static with route-map")
+
+ for addr_type in ADDR_TYPES:
+ input_redistribute = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_redistribute)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure aggregate-address to summarise all the advertised routes.")
+
+ for addr_type in ADDR_TYPES:
+ route_aggregate = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {"network": AGGREGATE_NW[addr_type], "as_set": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, route_aggregate)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that we see summarised route on router R3 with all the "
+ "community attribute values combined with that aggregate route."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"community": COMMUNITY[5]}
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove static routes as below: "
+ "(no) ip route 10.1.1.0/24 blackhole "
+ "(no) ip route 10.1.2.0/24 blackhole "
+ "(no) ipv6 route 10:1::1:0/120 blackhole "
+ "(no) ipv6 route 10:1::2:0/120 blackhole "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK_1_1[addr_type], NETWORK_1_2[addr_type]],
+ "next_hop": NEXT_HOP[addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R3 that whenever we remove the static routes, we still"
+ " see aggregated route however the corresponding community attribute"
+ "values are withdrawn."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"community": COMMUNITY[6]}
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Add/remove a new network with community value, each one from out of "
+ "aggregation range and other within aggregation range. "
+ )
+
+ step(
+ "Add a new network each one from out of aggregation range and "
+ "other within aggregation range. "
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK_3_1[addr_type], NETWORK_4_1[addr_type]],
+ "next_hop": NEXT_HOP[addr_type],
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_static)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ for (
+ pfx,
+ seq_id,
+ network,
+ ) in zip([6, 7], [60, 70], [NETWORK_3_1, NETWORK_4_1]):
+ prefix_list = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_{}_{}".format(addr_type, pfx): [
+ {
+ "seqid": seq_id,
+ "network": network[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, prefix_list)
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-map for applying prefix-list on r1")
+
+ for addr_type in ADDR_TYPES:
+ for pfx, comm_id in zip([6, 7], [7, 8]):
+ route_map = {
+ "r1": {
+ "route_maps": {
+ "rmap_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_{}_{}".format(
+ addr_type, pfx
+ )
+ }
+ },
+ "set": {"community": {"num": COMMUNITY[comm_id]}},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, route_map)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R3 when route is added within the summary range, aggregated"
+ " route also has associated community value added. However if the route"
+ " is beyond the summary range the aggregated route would have no impact"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {"community": COMMUNITY[9]}
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Delete", "Re-add"], [True, False]):
+ step("{} aggregation command from R1.".format(action))
+
+ for addr_type in ADDR_TYPES:
+ route_aggregate = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "aggregate_address": [
+ {
+ "network": AGGREGATE_NW[addr_type],
+ "as_set": True,
+ "delete": value,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, route_aggregate)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when as-set command is removed, we do not see community "
+ "attribute added to summarised route on R3. However when as-set option "
+ "is re-added, all the community attribute values must appear with "
+ "summarised route."
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_static_agg = {
+ "r1": {"static_routes": [{"network": AGGREGATE_NW[addr_type]}]}
+ }
+
+ if value:
+ result = verify_rib(
+ tgen, addr_type, "r1", input_static_agg, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r3", input_static_agg, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Aggregated route is still present \n Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_rib(tgen, addr_type, "r1", input_static_agg)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", input_static_agg)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict = {"community": COMMUNITY[9]}
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [AGGREGATE_NW[addr_type]], input_dict
+ )
+ assert result is True, "Test case {} : 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/bgp_route_map/bgp_route_map_topo1.json b/tests/topotests/bgp_route_map/bgp_route_map_topo1.json
new file mode 100644
index 0000000..e892639
--- /dev/null
+++ b/tests/topotests/bgp_route_map/bgp_route_map_topo1.json
@@ -0,0 +1,187 @@
+{
+ "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":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r4":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_route_map/bgp_route_map_topo2.json b/tests/topotests/bgp_route_map/bgp_route_map_topo2.json
new file mode 100755
index 0000000..c22a4c3
--- /dev/null
+++ b/tests/topotests/bgp_route_map/bgp_route_map_topo2.json
@@ -0,0 +1,316 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ }
+ }
+ },
+
+ "static_routes": [{
+ "network": "10.0.20.1/32",
+ "no_of_ip": 2,
+ "next_hop": "10.0.0.2"
+ },
+ {
+ "network": "1::1/128",
+ "no_of_ip": 2,
+ "next_hop": "fd00::2"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_route_map/test_route_map_topo1.py b/tests/topotests/bgp_route_map/test_route_map_topo1.py
new file mode 100644
index 0000000..655a3dc
--- /dev/null
+++ b/tests/topotests/bgp_route_map/test_route_map_topo1.py
@@ -0,0 +1,1409 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+import sys
+import time
+import pytest
+import os
+
+# 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.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+ create_route_maps,
+ create_static_routes,
+ create_prefix_lists,
+ check_address_types,
+ reset_config_on_routers,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+#################################
+# TOPOLOGY
+#################################
+"""
+
+ +-------+
+ +------- | R2 |
+ | +-------+
+ | |
+ +-------+ |
+ | R1 | |
+ +-------+ |
+ | |
+ | +-------+ +-------+
+ +---------- | R3 |----------| R4 |
+ +-------+ +-------+
+
+"""
+
+#################################
+# TEST SUMMARY
+#################################
+"""
+Following tests are covered to test route-map functionality:
+TC_34:
+ Verify if route-maps is applied in both inbound and
+ outbound direction to same neighbor/interface.
+TC_36:
+ Test permit/deny statements operation in route-maps with a
+ permutation and combination of permit/deny in prefix-lists
+TC_35:
+ Test multiple sequence numbers in a single route-map for different
+ match/set clauses.
+TC_37:
+ Test add/remove route-maps with multiple set
+ clauses and without any match statement.(Set only)
+TC_38:
+ Test add/remove route-maps with multiple match
+ clauses and without any set statement.(Match only)
+"""
+
+# Global variables
+bgp_convergence = False
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+
+# Global variables
+bgp_convergence = False
+NETWORK = {"ipv4": ["11.0.20.1/32", "20.0.20.1/32"], "ipv6": ["1::1/128", "2::1/128"]}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {"ipv4": "10.0.0.2", "ipv6": "fd00::2"}
+ADDR_TYPES = check_address_types()
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global ADDR_TYPES
+ 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...
+ json_file = "{}/bgp_route_map_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global bgp_convergence
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def test_route_map_inbound_outbound_same_neighbor_p0(request):
+ """
+ TC_34:
+ Verify if route-maps is applied in both inbound and
+ outbound direction to same neighbor/interface.
+ """
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for adt in ADDR_TYPES:
+
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][0],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][1],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_5 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "action": "permit",
+ "network": NETWORK["ipv4"][0],
+ }
+ ],
+ "pf_list_2_ipv4": [
+ {
+ "seqid": 10,
+ "action": "permit",
+ "network": NETWORK["ipv4"][1],
+ }
+ ],
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 100,
+ "action": "permit",
+ "network": NETWORK["ipv6"][0],
+ }
+ ],
+ "pf_list_2_ipv6": [
+ {
+ "seqid": 100,
+ "action": "permit",
+ "network": NETWORK["ipv6"][1],
+ }
+ ],
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_6 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ }
+ ],
+ "rmap_match_tag_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_2_{}".format(addr_type)
+ }
+ },
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "in",
+ },
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv6",
+ "direction": "in",
+ },
+ {
+ "name": "rmap_match_tag_1_ipv6",
+ "direction": "out",
+ },
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for adt in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_2 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [NETWORK[adt][1]],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, adt, dut, input_dict_2, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present in rib \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Verifying RIB routes
+ dut = "r4"
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK[adt][0]],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, adt, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present in rib \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+@pytest.mark.parametrize(
+ "prefix_action, rmap_action",
+ [("permit", "permit"), ("permit", "deny"), ("deny", "permit"), ("deny", "deny")],
+)
+def test_route_map_with_action_values_combination_of_prefix_action_p0(
+ request, prefix_action, rmap_action
+):
+ """
+ TC_36:
+ Test permit/deny statements operation in route-maps with a permutation and
+ combination of permit/deny in prefix-lists
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for adt in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][0],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Permit in perfix list and route-map
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": prefix_action}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": prefix_action}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": rmap_action,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_2 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK[adt][0]],
+ "no_of_ip": 9,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+
+ # tgen.mininet_cli()
+ if "deny" in [prefix_action, rmap_action]:
+ result = verify_rib(
+ tgen, adt, dut, input_dict_2, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nRoutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+ else:
+ result = verify_rib(tgen, adt, dut, input_dict_2, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+def test_route_map_multiple_seq_different_match_set_clause_p0(request):
+ """
+ TC_35:
+ Test multiple sequence numbers in a single route-map for different
+ match/set clauses.
+ """
+
+ tgen = get_topogen()
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for adt in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][0],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_2_{}".format(addr_type)
+ }
+ },
+ "set": {"path": {"as_num": 500}},
+ },
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_2_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ },
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for adt in ADDR_TYPES:
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_list1": [
+ {
+ "set": {
+ "metric": 50,
+ }
+ }
+ ],
+ }
+ }
+ }
+
+ static_routes = [NETWORK[adt][0]]
+
+ time.sleep(2)
+ result = verify_bgp_attributes(
+ tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r4"
+ result = verify_bgp_attributes(
+ tgen, adt, dut, static_routes, "rmap_match_pf_list1", input_dict
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Testcase " + tc_name + " :Passed \n")
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_route_map_set_only_no_match_p0(request):
+ """
+ TC_37:
+ Test add/remove route-maps with multiple set
+ clauses and without any match statement.(Set only)
+ """
+
+ tgen = get_topogen()
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for adt in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][0],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1": [
+ {
+ "action": "permit",
+ "set": {"metric": 50, "locPrf": 150, "weight": 4000},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ time.sleep(2)
+ for adt in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1": [
+ {
+ "action": "permit",
+ "set": {
+ "metric": 50,
+ },
+ }
+ ]
+ }
+ }
+ }
+ # Verifying RIB routes
+ static_routes = [NETWORK[adt][0]]
+ result = verify_bgp_attributes(
+ tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_attributes(
+ tgen, adt, "r4", static_routes, "rmap_match_pf_1", input_dict_4
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info("Testcase " + tc_name + " :Passed \n")
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_route_map_match_only_no_set_p0(request):
+ """
+ TC_38:
+ Test add/remove route-maps with multiple match
+ clauses and without any set statement.(Match only)
+ """
+
+ tgen = get_topogen()
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ reset_config_on_routers(tgen)
+
+ for adt in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": NETWORK[adt][0],
+ "no_of_ip": 1,
+ "next_hop": NEXT_HOP[adt],
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r1": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "metric": 50,
+ "locPrf": 150,
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create ip prefix list
+ input_dict_5 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_6 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_7 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_7)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for adt in ADDR_TYPES:
+ # Verifying RIB routes
+ static_routes = [NETWORK[adt][0]]
+ result = verify_bgp_attributes(
+ tgen, adt, "r3", static_routes, "rmap_match_pf_1", input_dict_3
+ )
+ assert result is True, "Test case {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_map/test_route_map_topo2.py b/tests/topotests/bgp_route_map/test_route_map_topo2.py
new file mode 100644
index 0000000..4da7eeb
--- /dev/null
+++ b/tests/topotests/bgp_route_map/test_route_map_topo2.py
@@ -0,0 +1,3981 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 route-map functionality.
+TC_57:
+ Create route map to match prefix-list and permit inbound
+ and outbound prefixes and set criteria on match
+TC_52:
+ Test modify set/match clauses in a route-map to see
+ if it takes immediate effect.
+TC_61:
+ Delete the route maps.
+TC_50_1:
+ Test modify/remove prefix-lists referenced by a
+ route-map for match statement.
+TC_50_2:
+ Remove prefix-list referencec by route-map match cluase
+ and verifying it reflecting as intended
+TC_51:
+ Add and remove community-list referencec by route-map match cluase
+ and verifying it reflecting as intended
+TC_45:
+ Test multiple match statements as part of a route-map"s single
+ sequence number. (Logical OR-ed of multiple match statements)
+TC_44:
+ Test multiple match statements as part of a route-map"s single
+ sequence number. (Logical AND of multiple match statements)
+TC_41:
+ Test add/remove route-maps to specific neighbor and see if
+ it takes effect as intended
+TC_56:
+ Test clear BGP sessions and interface flaps to see if
+ route-map properties are intact.
+TC_46:
+ Verify if a blank sequence number can be create(without any
+ match/set clause) and check if it allows all the traffic/prefixes
+TC_48:
+ Create route map setting local preference and weight to eBGP peeer
+ and metric to ibgp peer and verifying it should not get advertised
+TC_43:
+ Test multiple set statements as part of a route-map"s
+ single sequence number.
+TC_54:
+ Verify route-maps continue clause functionality.
+TC_55:
+ Verify route-maps goto clause functionality.
+TC_53:
+ Verify route-maps call clause functionality.
+TC_58:
+ Create route map deny inbound and outbound prefixes on
+ match prefix list and set criteria on match
+TC_59:
+ Create route map to permit inbound prefixes with filter
+ match tag and set criteria
+TC_60
+ Create route map to deny outbound prefixes with filter match tag,
+ and set criteria
+
+#################################
+# TOPOLOGY
+#################################
+
+ +-------+
+ +--------- | R2 |
+ | +-------+
+ |iBGP |
+ +-------+ |
+ | R1 | |iBGP
+ +-------+ |
+ | |
+ | iBGP +-------+ eBGP +-------+
+ +---------- | R3 |----------| R4 |
+ +-------+ +-------+
+ |
+ |eBGP
+ |
+ +-------+
+ | R5 |
+ +-------+
+
+
+"""
+
+import sys
+import time
+import pytest
+import inspect
+import os
+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, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ create_static_routes,
+ verify_rib,
+ delete_route_maps,
+ create_bgp_community_lists,
+ create_route_maps,
+ create_prefix_lists,
+ verify_route_maps,
+ check_address_types,
+ verify_bgp_community,
+ shutdown_bringup_interface,
+ verify_prefix_lists,
+ reset_config_on_routers,
+ verify_create_community_list,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+ verify_bgp_attributes,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+# Global variables
+bgp_convergence = False
+NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]}
+
+bgp_convergence = False
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+
+
+def setup_module(mod):
+ """setup_module.
+
+ Set up the pytest environment
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/bgp_route_map_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Checking BGP convergence
+ global bgp_convergence
+ global ADDR_TYPES
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "setup_module :Failed \n Error:" " {}".format(
+ bgp_convergence
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """teardown_module.
+
+ Teardown the pytest environment.
+ * `mod`: module name
+ """
+ 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)
+
+
+#####################################################
+# Tests starting
+#####################################################
+
+
+def test_rmap_match_prefix_list_permit_in_and_outbound_prefixes_p0():
+ """
+ TC: 57
+ Create route map to match prefix-list and permit inbound
+ and outbound prefixes and set criteria on match
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ for addr_type in ADDR_TYPES:
+ # Create route map
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {"prefix_lists": "pf_list_1_" + addr_type}
+ },
+ "set": {"locPrf": 150, "weight": 100},
+ },
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {"prefix_lists": "pf_list_1_" + addr_type}
+ },
+ "set": {"metric": 50},
+ },
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result4 = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_modify_set_match_clauses_in_rmap_p0():
+ """
+ TC_52:
+ Test modify set/match clauses in a route-map to see
+ if it takes immediate effect.
+ """
+
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
+ ],
+ "pf_list_2_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ],
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
+ ],
+ "pf_list_2_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ],
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result4 = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ # dual stack changes
+ for addr_type in ADDR_TYPES:
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result4
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify set/match clause of in-used route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 1000,
+ },
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 2000},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_delete_route_maps_p1():
+ """
+ TC_61:
+ Delete the route maps.
+ """
+
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {"action": "deny", "match": {addr_type: {"tag": "4001"}}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Delete route maps
+ for addr_type in ADDR_TYPES:
+ input_dict = {"r3": {"route_maps": ["rmap_match_tag_1_{}".format(addr_type)]}}
+ result = delete_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_route_maps(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_modify_prefix_list_referenced_by_rmap_p0():
+ """
+ TC_50_1:
+ Test modify/remove prefix-lists referenced by a
+ route-map for match statement.
+ """
+
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 100,
+ "network": "any",
+ "action": "permit",
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150, "weight": 100},
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Modify ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "deny"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nExpected behaviour: routes are not present \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_remove_prefix_list_referenced_by_rmap_p0():
+ """
+ TC_50_2:
+ Remove prefix-list referencec by route-map match cluase
+ and verifying it reflecting as intended
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Remove/Delete prefix list
+ input_dict_3 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ "delete": True,
+ }
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 100,
+ "network": "any",
+ "action": "permit",
+ "delete": True,
+ }
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_add_and_remove_community_list_referenced_by_rmap_p0():
+ """
+ TC_51:
+ Add and remove community-list referencec by route-map match cluase
+ and verifying it reflecting as intended
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Creating configuration from JSON
+ # build_config_from_json(tgen, topo)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_5 = {
+ "r1": {
+ "route_maps": {
+ "rm_r1_out_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_6 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rm_r1_out_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rm_r1_out_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Create standard large commumity-list
+ input_dict_1 = {
+ "r3": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "rmap_lcomm_{}".format(addr_type),
+ "value": "1:1:1 1:2:3 2:1:1 2:2:2",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verify BGP large community is created
+ result = verify_create_community_list(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Create route map
+ input_dict_2 = {
+ "r3": {
+ "route_maps": {
+ "rm_r3_in_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "large-community-list": {
+ "id": "rmap_lcomm_" + addr_type
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rm_r3_in_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rm_r3_in_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_3)
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verify large-community-list
+ dut = "r3"
+ networks = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ input_dict_4 = {"largeCommunity": "1:1:1 1:2:3 2:1:1 2:2:2"}
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, addr_type, dut, networks[addr_type], input_dict_4
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_multiple_match_statement_in_route_map_logical_ORed_p0():
+ """
+ TC_45:
+ Test multiple match statements as part of a route-map"s single
+ sequence number. (Logical OR-ed of multiple match statements)
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Api call to advertise networks
+ input_dict_nw1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"advertise_networks": [{"network": "10.0.30.1/32"}]}
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "1::1/128"}]}
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_nw1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call to advertise networks
+ input_dict_nw2 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {"advertise_networks": [{"network": "20.0.30.1/32"}]}
+ },
+ "ipv6": {
+ "unicast": {"advertise_networks": [{"network": "2::1/128"}]}
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_nw2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_2_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_2_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_3_addr_type = {}
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150},
+ }
+ ]
+ }
+ }
+ }
+ input_dict_3_addr_type[addr_type] = input_dict_3
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 200},
+ }
+ ]
+ }
+ }
+ }
+ input_dict_3_addr_type[addr_type] = input_dict_3
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_6 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {"ipv4": ["10.0.30.1/32"], "ipv6": ["1::1/128"]}
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ routes[addr_type],
+ rmap_name,
+ input_dict_3_addr_type[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ routes = {"ipv4": ["20.0.30.1/32"], "ipv6": ["2::1/128"]}
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_multiple_match_statement_in_route_map_logical_ANDed_p1():
+ """
+ TC_44:
+ Test multiple match statements as part of a route-map"s single
+ sequence number. (Logical AND of multiple match statements)
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_5 = {
+ "r1": {
+ "route_maps": {
+ "rm_r1_out_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {
+ "large_community": {"num": "1:1:1 1:2:3 2:1:1 2:2:2"}
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_5)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ for addr_type in ADDR_TYPES:
+ input_dict_6 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rm_r1_out_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_6)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ # Create standard large commumity-list
+ input_dict_1 = {
+ "r3": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "rmap_lcomm_{}".format(addr_type),
+ "value": "1:1:1 1:2:3 2:1:1 2:2:2",
+ "large": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verify BGP large community is created
+ result = verify_create_community_list(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Create route map
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "large_community_list": {
+ "id": "rmap_lcomm_" + addr_type
+ }
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ # Configure neighbor for route map
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_{}".format(
+ addr_type
+ ),
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ # sleep(10)
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_add_remove_rmap_to_specific_neighbor_p0():
+ """
+ TC_41:
+ Test add/remove route-maps to specific neighbor and see if
+ it takes effect as intended
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "deny"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error Routes are still present: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Remove applied rmap from neighbor
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_clear_bgp_and_flap_interface_to_verify_rmap_properties_p0():
+ """
+ TC_56:
+ Test clear BGP sessions and interface flaps to see if
+ route-map properties are intact.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "5",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150, "weight": 100},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # clear bgp, so config changes would be reflected
+ dut = "r3"
+ result = clear_bgp_and_verify(tgen, topo, dut)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Flap interface to see if route-map properties are intact
+ # Shutdown interface
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ sleep(5)
+
+ # Bringup interface
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # Verify BGP convergence once interface is up
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_rmap_without_match_and_set_clause_p0():
+ """
+ TC_46:
+ Verify if a blank sequence number can be create(without any
+ match/set clause) and check if it allows all the traffic/prefixes
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_no_match_set_1_{}".format(addr_type): [
+ {"action": "permit", "seq_id": "5"}
+ ],
+ "rmap_no_match_set_2_{}".format(addr_type): [
+ {"action": "deny", "seq_id": "5"}
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_no_match_set_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_no_match_set_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_no_match_set_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_no_match_set_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_set_localpref_weight_to_ebgp_and_med_to_ibgp_peers_p0():
+ """
+ TC_48:
+ Create route map setting local preference and weight to eBGP peeer
+ and metric to ibgp peer and verifying it should not get advertised
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3_addr_type = {}
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150},
+ }
+ ],
+ "rmap_match_pf_3_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"weight": 1000},
+ }
+ ],
+ }
+ }
+ }
+ input_dict_3_addr_type[addr_type] = input_dict_3
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_3_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_3_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ rmap_name = "rmap_match_pf_1"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r4"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ rmap_name = "rmap_match_pf_2"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ routes[addr_type],
+ rmap_name,
+ input_dict_3_addr_type[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nAttributes are not set \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Verifying RIB routes
+ dut = "r5"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ # Verifying BGP set attributes
+ dut = "r5"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+
+ rmap_name = "rmap_match_pf_3"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_3_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ routes[addr_type],
+ rmap_name,
+ input_dict_3_addr_type[addr_type],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nAttributes are not set \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_multiple_set_on_single_sequence_in_rmap_p0():
+ """
+ TC_43:
+ Test multiple set statements as part of a route-map"s
+ single sequence number.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150, "weight": 100, "metric": 50},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+
+ rmap_name = "rmap_match_pf_1"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_route_maps_with_continue_clause_p0():
+ """
+ TC_54:
+ Verify route-maps continue clause functionality.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150},
+ "continue": "30",
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 200},
+ },
+ {
+ "action": "permit",
+ "seq_id": "30",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 100},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ rmap_name = "rmap_match_pf_1"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]}
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ routes[addr_type],
+ rmap_name,
+ input_dict_3,
+ seq_id[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_route_maps_with_goto_clause_p0():
+ """
+ TC_55:
+ Verify route-maps goto clause functionality.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "goto": "30",
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 100},
+ },
+ {
+ "action": "permit",
+ "seq_id": "30",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 200},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ # tgen.mininet_cli()
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ rmap_name = "rmap_match_pf_1"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ seq_id = {"ipv4": ["10", "30"], "ipv6": ["10", "30"]}
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ routes[addr_type],
+ rmap_name,
+ input_dict_3,
+ seq_id[addr_type],
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_route_maps_with_call_clause_p0():
+ """
+ TC_53:
+ Verify route-maps call clause functionality.
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 150},
+ "call": "rmap_match_pf_2_{}".format(addr_type),
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 200},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv6",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verifying BGP set attributes
+ dut = "r3"
+ routes = {
+ "ipv4": ["10.0.20.1/32", "10.0.20.2/32"],
+ "ipv6": ["1::1/128", "1::2/128"],
+ }
+ rmap_name = "rmap_match_pf_1"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_1_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ rmap_name = "rmap_match_pf_2"
+ for addr_type in ADDR_TYPES:
+ rmap_name = "rmap_match_pf_2_{}".format(addr_type)
+ result = verify_bgp_attributes(
+ tgen, addr_type, dut, routes[addr_type], rmap_name, input_dict_3
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_create_rmap_match_prefix_list_to_deny_in_and_outbound_prefixes_p0():
+ """
+ TC_58:
+ Create route map deny inbound and outbound prefixes on
+ match prefix list and set criteria on match
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r3": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ },
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 100, "network": "any", "action": "permit"}
+ ]
+ },
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ for addr_type in ADDR_TYPES:
+ input_dict_3 = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "locPrf": 150,
+ },
+ }
+ ],
+ "rmap_match_pf_2_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ "set": {"metric": 50},
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_1_ipv4",
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {
+ "route_maps": [
+ {
+ "name": "rmap_match_pf_2_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+ input_dict = topo["routers"]
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ # Verifying RIB routes
+ dut = "r4"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are not present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_create_rmap_to_match_tag_permit_inbound_prefixes_p0():
+ """
+ TC_59:
+ Create route map to permit inbound prefixes with filter
+ match tag and set criteria
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {"action": "permit", "match": {addr_type: {"tag": "4001"}}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001}
+ ]
+ }
+ }
+ result = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+def test_create_rmap_to_match_tag_deny_outbound_prefixes_p0():
+ """
+ TC_60
+ Create route map to deny outbound prefixes with filter match tag,
+ and set criteria
+ """
+ tgen = get_topogen()
+ global bgp_convergence
+
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = inspect.stack()[0][3]
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Create Static routes
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Api call to redistribute static routes
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "local_as": 100,
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"},
+ ]
+ }
+ },
+ },
+ }
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r1": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {"action": "deny", "match": {addr_type: {"tag": "4001"}}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv6",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Verifying RIB routes
+ dut = "r3"
+ protocol = "bgp"
+
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK[addr_type], "next_hop": "Null0", "tag": 4001}
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nroutes are denied \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_map_delay_timer/__init__.py b/tests/topotests/bgp_route_map_delay_timer/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/__init__.py
diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf
new file mode 100644
index 0000000..04dc73a
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/r1/bgpd.conf
@@ -0,0 +1,24 @@
+!
+debug bgp updates
+debug bgp neighbor
+!
+bgp route-map delay-timer 5
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.1.2 remote-as external
+ address-family ipv4 unicast
+ network 10.10.10.1/32
+ network 10.10.10.2/32
+ network 10.10.10.3/32
+ aggregate-address 10.10.10.0/24 summary-only
+ neighbor 192.168.1.2 unsuppress-map r2
+ exit-address-family
+!
+ip prefix-list r1 seq 5 permit 10.10.10.1/32
+ip prefix-list r1 seq 10 permit 10.10.10.2/32
+!
+route-map r2 permit 10
+ match ip address prefix-list r1
+exit
diff --git a/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf
new file mode 100644
index 0000000..36653e6
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/r2/bgpd.conf
@@ -0,0 +1,4 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py
new file mode 100644
index 0000000..15a077d
--- /dev/null
+++ b/tests/topotests/bgp_route_map_delay_timer/test_bgp_route_map_delay_timer.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: ISC
+
+# Copyright (c) 2023 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+
+"""
+
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_delay_timer():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge_1():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.0/24": {},
+ "10.10.10.1/32": {},
+ "10.10.10.2/32": {},
+ "10.10.10.3/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge_1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "10.10.10.3/32 should not be advertised to r2"
+
+ # Set route-map delay-timer to max value and remove 10.10.10.2/32.
+ # After this, r1 MUST do not announce updates immediately, and wait
+ # 600 seconds before withdrawing 10.10.10.2/32.
+ r2.vtysh_cmd(
+ """
+ configure terminal
+ bgp route-map delay-timer 600
+ no ip prefix-list r1 seq 10 permit 10.10.10.2/32
+ """
+ )
+
+ def _bgp_converge_2():
+ output = json.loads(
+ r1.vtysh_cmd(
+ "show bgp ipv4 unicast neighbor 192.168.1.2 advertised-routes json"
+ )
+ )
+ expected = {
+ "advertisedRoutes": {
+ "10.10.10.0/24": {},
+ "10.10.10.1/32": {},
+ "10.10.10.2/32": None,
+ "10.10.10.3/32": None,
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ # We are checking `not None` here to wait count*wait time and if we have different
+ # results than expected, it means good - 10.10.10.2/32 wasn't withdrawn immediately.
+ test_func = functools.partial(_bgp_converge_2)
+ _, result = topotest.run_and_expect(test_func, not None, count=60, wait=0.5)
+ assert (
+ result is not None
+ ), "10.10.10.2/32 advertised, but should not be advertised to r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py b/tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/__init__.py
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf
new file mode 100644
index 0000000..8b743bd
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/bgpd.conf
@@ -0,0 +1,34 @@
+!
+bgp send-extra-data zebra
+!
+ipv6 access-list nh1 permit 2001:db8:1::/64
+ipv6 access-list nh2 permit 2001:db8:2::/64
+ipv6 access-list nh3 permit 2001:db8:3::/64
+!
+ipv6 prefix-list nh4 permit 2001:db8:5::/64 le 128
+!
+router bgp 65001
+ bgp router-id 10.10.10.1
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8::2 remote-as external
+ address-family ipv6 unicast
+ neighbor 2001:db8::2 activate
+ neighbor 2001:db8::2 route-map r2 in
+ exit-address-family
+!
+route-map r2 permit 10
+ match ipv6 next-hop nh1
+ set community 65002:1
+route-map r2 permit 20
+ match ipv6 next-hop nh2
+ set community 65002:2
+route-map r2 permit 30
+ match ipv6 next-hop nh3
+ set community 65002:3
+route-map r2 permit 40
+ match ipv6 next-hop address 2001:db8:4::1
+ set community 65002:4
+route-map r2 permit 50
+ match ipv6 next-hop prefix-list nh4
+ set community 65002:5
+!
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf
new file mode 100644
index 0000000..1d4374b
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ipv6 address 2001:db8::1/64
+!
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf
new file mode 100644
index 0000000..61c36d3
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/bgpd.conf
@@ -0,0 +1,35 @@
+!
+bgp send-extra-data zebra
+!
+router bgp 65002
+ bgp router-id 10.10.10.2
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8::1 remote-as external
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8::1 activate
+ neighbor 2001:db8::1 route-map r1 out
+ exit-address-family
+!
+ipv6 prefix-list p1 permit 2001:db8:1::1/128
+ipv6 prefix-list p2 permit 2001:db8:2::1/128
+ipv6 prefix-list p3 permit 2001:db8:3::1/128
+ipv6 prefix-list p4 permit 2001:db8:4::1/128
+ipv6 prefix-list p5 permit 2001:db8:5::1/128
+!
+route-map r1 permit 10
+ match ipv6 address prefix-list p1
+ set ipv6 next-hop global 2001:db8:1::1
+route-map r1 permit 20
+ match ipv6 address prefix-list p2
+ set ipv6 next-hop global 2001:db8:2::1
+route-map r1 permit 30
+ match ipv6 address prefix-list p3
+ set ipv6 next-hop global 2001:db8:3::1
+route-map r1 permit 40
+ match ipv6 address prefix-list p4
+ set ipv6 next-hop global 2001:db8:4::1
+route-map r1 permit 50
+ match ipv6 address prefix-list p5
+ set ipv6 next-hop global 2001:db8:5::1
+!
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf
new file mode 100644
index 0000000..e69345f
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/r2/zebra.conf
@@ -0,0 +1,11 @@
+!
+int lo
+ ipv6 address 2001:db8:1::1/128
+ ipv6 address 2001:db8:2::1/128
+ ipv6 address 2001:db8:3::1/128
+ ipv6 address 2001:db8:4::1/128
+ ipv6 address 2001:db8:5::1/128
+!
+int r2-eth0
+ ip address 2001:db8::2/64
+!
diff --git a/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py b/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py
new file mode 100644
index 0000000..8c86526
--- /dev/null
+++ b/tests/topotests/bgp_route_map_match_ipv6_nexthop/test_bgp_route_map_match_ipv6_nexthop.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if we can match BGP prefixes by next-hop which is
+specified by an IPv6 Access-list, prefix-list or just an address.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_map_match_ipv6_next_hop_access_list():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ipv6 route json"))
+ expected = {
+ "2001:db8:1::1/128": [
+ {
+ "communities": "65002:1",
+ }
+ ],
+ "2001:db8:2::1/128": [
+ {
+ "communities": "65002:2",
+ }
+ ],
+ "2001:db8:3::1/128": [
+ {
+ "communities": "65002:3",
+ }
+ ],
+ "2001:db8:4::1/128": [
+ {
+ "communities": "65002:4",
+ }
+ ],
+ "2001:db8:5::1/128": [
+ {
+ "communities": "65002:5",
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't match routes using ipv6 next-hop access-list"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf
new file mode 100644
index 0000000..1929dfa
--- /dev/null
+++ b/tests/topotests/bgp_route_origin_parser/pe1/bgpd.conf
@@ -0,0 +1,2 @@
+router bgp 65001
+ neighbor 192.168.2.1 remote-as external
diff --git a/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py
new file mode 100644
index 0000000..673efc2
--- /dev/null
+++ b/tests/topotests/bgp_route_origin_parser/test_bgp_route_origin_parser.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# April 03 2023, Trey Aspelund <taspelund@nvidia.com>
+#
+# Copyright (C) 2023 NVIDIA Corporation
+#
+# Test if the CLI parser for RT/SoO ecoms correctly
+# constrain user input to valid 4-byte ASN values.
+#
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("pe1")
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ pe1 = tgen.gears["pe1"]
+ pe1.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "pe1/bgpd.conf"))
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_origin_parser():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pe1 = tgen.gears["pe1"]
+
+ def _invalid_soo_accepted():
+ pe1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ address-family ipv4 unicast
+ neighbor 192.168.2.1 soo 4294967296:65
+ """
+ )
+ run_cfg = pe1.vtysh_cmd("show run")
+ return "soo" in run_cfg
+
+ def _max_soo_accepted():
+ pe1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ address-family ipv4 unicast
+ neighbor 192.168.2.1 soo 4294967295:65
+ """
+ )
+ run_cfg = pe1.vtysh_cmd("show run")
+ return "soo 4294967295:65" in run_cfg
+
+ def _invalid_rt_accepted():
+ pe1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ address-family ipv4 unicast
+ rt vpn both 4294967296:65
+ """
+ )
+ run_cfg = pe1.vtysh_cmd("show run")
+ return "rt vpn" in run_cfg
+
+ def _max_rt_accepted():
+ pe1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ address-family ipv4 unicast
+ rt vpn both 4294967295:65
+ """
+ )
+ run_cfg = pe1.vtysh_cmd("show run")
+ return "rt vpn both 4294967295:65" in run_cfg
+
+ step(
+ "Configure invalid 4-byte value SoO (4294967296:65), this should not be accepted"
+ )
+ test_func = functools.partial(_invalid_soo_accepted)
+ _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5)
+ assert result is False, "invalid 4-byte value of SoO accepted"
+
+ step("Configure max 4-byte value SoO (4294967295:65), this should be accepted")
+ test_func = functools.partial(_max_soo_accepted)
+ _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5)
+ assert result is True, "max 4-byte value of SoO not accepted"
+
+ step(
+ "Configure invalid 4-byte value RT (4294967296:65), this should not be accepted"
+ )
+ test_func = functools.partial(_invalid_rt_accepted)
+ _, result = topotest.run_and_expect(test_func, False, count=30, wait=0.5)
+ assert result is False, "invalid 4-byte value of RT accepted"
+
+ step("Configure max 4-byte value RT (4294967295:65), this should be accepted")
+ test_func = functools.partial(_max_rt_accepted)
+ _, result = topotest.run_and_expect(test_func, True, count=30, wait=0.5)
+ assert result is True, "max 4-byte value of RT not accepted"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_route_server_client/__init__.py b/tests/topotests/bgp_route_server_client/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/__init__.py
diff --git a/tests/topotests/bgp_route_server_client/r1/bgpd.conf b/tests/topotests/bgp_route_server_client/r1/bgpd.conf
new file mode 100644
index 0000000..9826b67
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r1/bgpd.conf
@@ -0,0 +1,12 @@
+!
+router bgp 65001
+ bgp router-id 10.10.10.1
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::1 remote-as external
+ neighbor 2001:db8:1::1 timers 3 10
+ neighbor 2001:db8:1::1 timers connect 5
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_route_server_client/r1/zebra.conf b/tests/topotests/bgp_route_server_client/r1/zebra.conf
new file mode 100644
index 0000000..08d0be6
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ipv6 address 2001:db8:f::1/128
+!
+int r1-eth0
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/bgp_route_server_client/r2/bgpd.conf b/tests/topotests/bgp_route_server_client/r2/bgpd.conf
new file mode 100644
index 0000000..3b0a24b
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r2/bgpd.conf
@@ -0,0 +1,17 @@
+router bgp 65000 view RS
+ bgp router-id 10.10.10.2
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:1::2 remote-as external
+ neighbor 2001:db8:1::2 timers 3 10
+ neighbor 2001:db8:1::2 timers connect 5
+ neighbor 2001:db8:3::2 remote-as external
+ neighbor 2001:db8:3::2 timers 3 10
+ neighbor 2001:db8:3::2 timers connect 5
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:1::2 activate
+ neighbor 2001:db8:3::2 activate
+ neighbor 2001:db8:1::2 route-server-client
+ neighbor 2001:db8:3::2 route-server-client
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_route_server_client/r2/zebra.conf b/tests/topotests/bgp_route_server_client/r2/zebra.conf
new file mode 100644
index 0000000..806bc4f
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+int r2-eth0
+ ipv6 address 2001:db8:1::1/64
+!
+int r2-eth1
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/bgp_route_server_client/r3/bgpd.conf b/tests/topotests/bgp_route_server_client/r3/bgpd.conf
new file mode 100644
index 0000000..60a5ffc
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r3/bgpd.conf
@@ -0,0 +1,12 @@
+!
+router bgp 65003
+ bgp router-id 10.10.10.3
+ no bgp ebgp-requires-policy
+ neighbor 2001:db8:3::1 remote-as external
+ neighbor 2001:db8:3::1 timers 3 10
+ neighbor 2001:db8:3::1 timers connect 5
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor 2001:db8:3::1 activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_route_server_client/r3/zebra.conf b/tests/topotests/bgp_route_server_client/r3/zebra.conf
new file mode 100644
index 0000000..b5511a4
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+int lo
+ ipv6 address 2001:db8:f::3/128
+!
+int r3-eth0
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py
new file mode 100644
index 0000000..21088f7
--- /dev/null
+++ b/tests/topotests/bgp_route_server_client/test_bgp_route_server_client.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if we send ONLY GUA address for route-server-client peers.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+pytestmark = pytest.mark.bgpd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route_server_client():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv6 unicast summary json"))
+ expected = {"peers": {"2001:db8:1::1": {"state": "Established", "pfxRcd": 2}}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP sessions to be up"
+
+ def _bgp_prefix_received(router):
+ output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json"))
+ expected = {
+ "prefix": "2001:db8:f::3/128",
+ "paths": [{"nexthops": [{"ip": "2001:db8:3::2"}]}],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_prefix_received, r1)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP GUA next hop from r3 in r1"
+
+ def _bgp_single_next_hop(router):
+ output = json.loads(router.vtysh_cmd("show bgp 2001:db8:f::3/128 json"))
+ return len(output["paths"][0]["nexthops"])
+
+ assert (
+ _bgp_single_next_hop(r1) == 1
+ ), "Not ONLY one Next Hop received for 2001:db8:f::3/128"
+
+ def _bgp_gua_lla_next_hop(router):
+ output = json.loads(router.vtysh_cmd("show bgp view RS 2001:db8:f::3/128 json"))
+ expected = {
+ "prefix": "2001:db8:f::3/128",
+ "paths": [
+ {
+ "nexthops": [
+ {
+ "ip": "2001:db8:3::2",
+ "hostname": "r3",
+ "afi": "ipv6",
+ "scope": "global",
+ },
+ {"hostname": "r3", "afi": "ipv6", "scope": "link-local"},
+ ]
+ }
+ ],
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_gua_lla_next_hop, r2)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Cannot see BGP LLA next hop from r3 in r2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf
new file mode 100644
index 0000000..cece3fc
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/spine1/bgpd.conf
@@ -0,0 +1,11 @@
+hostname spine1
+router bgp 99
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as internal
+ neighbor 192.168.2.1 timers 3 10
+ neighbor 192.168.4.2 remote-as internal
+ neighbor 192.168.4.2 timers 3 10
+ address-family ipv4 uni
+ redistribute connected
+ neighbor 192.168.2.1 route-reflector-client
+ neighbor 192.168.4.2 route-reflector-client
diff --git a/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref
new file mode 100644
index 0000000..75ce1b1
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/spine1/show_ip_route.json_ref
@@ -0,0 +1,144 @@
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.2.1",
+ "afi":"ipv4",
+ "interfaceName":"spine1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"spine1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0\/24":[
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.4.2",
+ "afi":"ipv4",
+ "interfaceName":"spine1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.4.0\/24":[
+ {
+ "prefix":"192.168.4.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"spine1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.5.1\/32":[
+ {
+ "prefix":"192.168.5.1\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.2.1",
+ "afi":"ipv4",
+ "interfaceName":"spine1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.6.2\/32":[
+ {
+ "prefix":"192.168.6.2\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.4.2",
+ "afi":"ipv4",
+ "interfaceName":"spine1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf
new file mode 100644
index 0000000..ea25462
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/spine1/zebra.conf
@@ -0,0 +1,9 @@
+hostname spine1
+ip forwarding
+ipv6 forwarding
+
+int spine1-eth0
+ ip addr 192.168.2.3/24
+
+int spine1-eth1
+ ip addr 192.168.4.3/24
diff --git a/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
new file mode 100644
index 0000000..cf8315f
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/test_bgp_rr_ibgp_topo1.py
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_rr_ibgp_topo1.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_rr_ibgp_topo1.py: Testing IBGP with RR and no IGP
+
+Ensure that a basic rr topology comes up and correctly passes
+routes around
+
+"""
+
+import os
+import sys
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ tgen.add_router("tor1")
+ tgen.add_router("tor2")
+ tgen.add_router("spine1")
+
+ # First switch is for a dummy interface (for local network)
+ # on tor1
+ # 192.168.1.0/24
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["tor1"])
+
+ # 192.168.2.0/24 - tor1 <-> spine1 connection
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["tor1"])
+ switch.add_link(tgen.gears["spine1"])
+
+ # 3rd switch is for a dummy interface (for local netwokr)
+ # 192.168.3.0/24 - tor2
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["tor2"])
+
+ # 192.168.4.0/24 - tor2 <-> spine1 connection
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["tor2"])
+ switch.add_link(tgen.gears["spine1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ 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 test_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ topotest.sleep(5, "Waiting for BGP_RR_IBGP convergence")
+
+
+def test_bgp_rr_ibgp_routes():
+ "Test Route Reflection"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Verify BGP_RR_IBGP Status
+ logger.info("Verifying BGP_RR_IBGP routes")
+
+
+def test_zebra_ipv4_routingTable():
+ "Test 'show ip route'"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ failures = 0
+ router_list = tgen.routers().values()
+ for router in router_list:
+ output = router.vtysh_cmd("show ip route json", isjson=True)
+ refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name)
+ expected = json.loads(open(refTableFile).read())
+
+ assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format(
+ router.name
+ )
+ assert topotest.json_cmp(output, expected) is None, assertmsg
+
+
+def test_shutdown_check_stderr():
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ pytest.skip("Skipping test for Stderr output and memory leaks")
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Verifying unexpected STDERR output from daemons")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ router.stop()
+
+ log = tgen.net[router.name].getStdErr("bgpd")
+ if log:
+ logger.error("BGPd StdErr Log:" + log)
+ log = tgen.net[router.name].getStdErr("zebra")
+ if log:
+ logger.error("Zebra StdErr Log:" + log)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
+
+#
+# Auxiliary Functions
+#
diff --git a/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf
new file mode 100644
index 0000000..1b9f150
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor1/bgpd.conf
@@ -0,0 +1,6 @@
+hostname tor1
+router bgp 99
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.3 remote-as internal
+ neighbor 192.168.2.3 timers 3 10
+ redistribute connected
diff --git a/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref
new file mode 100644
index 0000000..6cfa024
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor1/show_ip_route.json_ref
@@ -0,0 +1,157 @@
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"tor1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"tor1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0\/24":[
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":5,
+ "ip":"192.168.4.2",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.2.3",
+ "afi":"ipv4",
+ "interfaceName":"tor1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.4.0\/24":[
+ {
+ "prefix":"192.168.4.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.2.3",
+ "afi":"ipv4",
+ "interfaceName":"tor1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.5.1\/32":[
+ {
+ "prefix":"192.168.5.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.6.2\/32":[
+ {
+ "prefix":"192.168.6.2\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":5,
+ "ip":"192.168.4.2",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.2.3",
+ "afi":"ipv4",
+ "interfaceName":"tor1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf
new file mode 100644
index 0000000..25b4fcf
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor1/zebra.conf
@@ -0,0 +1,12 @@
+hostname tor1
+ip forwarding
+ipv6 forwarding
+
+int tor1-eth0
+ ip addr 192.168.1.1/24
+
+int tor1-eth1
+ ip addr 192.168.2.1/24
+
+int lo
+ ip addr 192.168.5.1/32
diff --git a/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf
new file mode 100644
index 0000000..3bdb359
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor2/bgpd.conf
@@ -0,0 +1,6 @@
+hostname tor2
+router bgp 99
+ no bgp ebgp-requires-policy
+ neighbor 192.168.4.3 remote-as internal
+ neighbor 192.168.4.3 timers 3 10
+ redistribute connected
diff --git a/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref
new file mode 100644
index 0000000..d9e9290
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor2/show_ip_route.json_ref
@@ -0,0 +1,157 @@
+{
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":5,
+ "ip":"192.168.2.1",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.4.3",
+ "afi":"ipv4",
+ "interfaceName":"tor2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0\/24":[
+ {
+ "prefix":"192.168.2.0\/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.4.3",
+ "afi":"ipv4",
+ "interfaceName":"tor2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0\/24":[
+ {
+ "prefix":"192.168.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"tor2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.4.0\/24":[
+ {
+ "prefix":"192.168.4.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"tor2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.5.1\/32":[
+ {
+ "prefix":"192.168.5.1\/32",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":200,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":13,
+ "nexthops":[
+ {
+ "flags":5,
+ "ip":"192.168.2.1",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true
+ },
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.4.3",
+ "afi":"ipv4",
+ "interfaceName":"tor2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.6.2\/32":[
+ {
+ "prefix":"192.168.6.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf
new file mode 100644
index 0000000..e1a06b1
--- /dev/null
+++ b/tests/topotests/bgp_rr_ibgp/tor2/zebra.conf
@@ -0,0 +1,13 @@
+hostname tor2
+ip forwarding
+ipv6 forwarding
+
+int tor2-eth0
+ ip addr 192.168.3.2/24
+
+int tor2-eth1
+ ip addr 192.168.4.2/24
+
+
+int lo
+ ip addr 192.168.6.2/32
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/__init__.py b/tests/topotests/bgp_sender_as_path_loop_detection/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/__init__.py
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
new file mode 100644
index 0000000..719d763
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/bgpd.conf
@@ -0,0 +1,14 @@
+! exit1
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.255.1 route-map prepend out
+ redistribute connected
+ exit-address-family
+ !
+!
+route-map prepend permit 10
+ set as-path prepend 65003
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
new file mode 100644
index 0000000..9904bb4
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r1/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
new file mode 100644
index 0000000..a4a654d
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/bgpd.conf
@@ -0,0 +1,11 @@
+! spine
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.2 solo
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.254.2 timers 3 10
+ neighbor 192.168.254.2 solo
+ neighbor 192.168.254.2 sender-as-path-loop-detection
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf
new file mode 100644
index 0000000..f0d357c
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r2/zebra.conf
@@ -0,0 +1,9 @@
+! spine
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+interface r2-eth1
+ ip address 192.168.254.1/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
new file mode 100644
index 0000000..2e24de0
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r3/bgpd.conf
@@ -0,0 +1,6 @@
+! exit2
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.254.1 remote-as 65002
+ neighbor 192.168.254.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf b/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf
new file mode 100644
index 0000000..a10fe3a
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/r3/zebra.conf
@@ -0,0 +1,6 @@
+! exit2
+interface r3-eth0
+ ip address 192.168.254.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
new file mode 100644
index 0000000..b5c33f3
--- /dev/null
+++ b/tests/topotests/bgp_sender_as_path_loop_detection/test_bgp_sender-as-path-loop-detection.py
@@ -0,0 +1,143 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_sender-as-path-loop-detection.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if neighbor <neighbor> sender-as-path-loop-detection
+command works as expeced.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_sender_as_path_loop_detection():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_has_route_from_r1(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp 172.16.255.254/32 json"))
+ expected = {
+ "paths": [
+ {
+ "aspath": {
+ "segments": [{"type": "as-sequence", "list": [65001, 65003]}],
+ "length": 2,
+ }
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_suppress_route_to_r3(router):
+ output = json.loads(
+ router.vtysh_cmd(
+ "show ip bgp neighbor 192.168.254.2 advertised-routes json"
+ )
+ )
+ expected = {"totalPrefixCounter": 0}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_has_route_from_r1, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert result is None, 'Failed to see a route from r1 in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_suppress_route_to_r3, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+
+ assert (
+ result is None
+ ), 'Route 172.16.255.254/32 should not be sent to r3 "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_set_aspath_replace/__init__.py b/tests/topotests/bgp_set_aspath_replace/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/__init__.py
diff --git a/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf
new file mode 100644
index 0000000..1e98f4e
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r1/bgpd.conf
@@ -0,0 +1,17 @@
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 3 10
+ address-family ipv4 unicast
+ neighbor 192.168.1.2 route-map r2 in
+ exit-address-family
+!
+ip prefix-list p1 seq 5 permit 172.16.255.31/32
+!
+route-map r2 permit 10
+ match ip address prefix-list p1
+ set as-path replace 65003
+route-map r2 permit 20
+ set as-path replace any
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf
new file mode 100644
index 0000000..acf120b
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf
new file mode 100644
index 0000000..23367f9
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r2/bgpd.conf
@@ -0,0 +1,8 @@
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf
new file mode 100644
index 0000000..f229954
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf b/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf
new file mode 100644
index 0000000..b7a7ced
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r3/bgpd.conf
@@ -0,0 +1,9 @@
+!
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf b/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf
new file mode 100644
index 0000000..3fa6c64
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/r3/zebra.conf
@@ -0,0 +1,10 @@
+!
+int lo
+ ip address 172.16.255.31/32
+ ip address 172.16.255.32/32
+!
+interface r3-eth0
+ ip address 192.168.2.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py
new file mode 100644
index 0000000..d5549ae
--- /dev/null
+++ b/tests/topotests/bgp_set_aspath_replace/test_bgp_set_aspath_replace.py
@@ -0,0 +1,103 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_set_aspath_replace.py
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if `set as-path replace` is working correctly for route-maps.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_maximum_prefix_out():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "172.16.255.31/32": [{"path": "65002 65001"}],
+ "172.16.255.32/32": [{"path": "65001 65001"}],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, tgen.gears["r1"])
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+
+ assert result is None, "Failed overriding incoming AS-PATH with route-map"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/__init__.py b/tests/topotests/bgp_set_local_preference_add_subtract/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/__init__.py
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf
new file mode 100644
index 0000000..57e2f58
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65000
+ neighbor 192.168.255.2 timers 3 10
+ neighbor 192.168.255.3 remote-as 65000
+ neighbor 192.168.255.3 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf
new file mode 100644
index 0000000..6e9b0b4
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf
new file mode 100644
index 0000000..1f85a35
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r2/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ network 10.10.10.2/32 route-map l2
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ address-family ipv4
+ redistribute connected
+ neighbor 192.168.255.1 route-map r1-out out
+ exit-address-family
+!
+route-map r1-out permit 10
+ set local-preference +50
+route-map l2 permit 10
+ set local-preference +10
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf
new file mode 100644
index 0000000..93e3590
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf
new file mode 100644
index 0000000..49b8f1c
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r3/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ no bgp network import-check
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ network 10.10.10.3/32 route-map l3
+ address-family ipv4
+ redistribute connected
+ neighbor 192.168.255.1 route-map r1-out out
+ exit-address-family
+!
+route-map r1-out permit 10
+ set local-preference -50
+route-map l3 permit 10
+ set local-preference -10
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf b/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf
new file mode 100644
index 0000000..b5e060c
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/r3/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r3-eth0
+ ip address 192.168.255.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
new file mode 100644
index 0000000..ec9c8a4
--- /dev/null
+++ b/tests/topotests/bgp_set_local_preference_add_subtract/test_bgp_set_local-preference_add_subtract.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+#
+# bgp_set_local-preference_add_subtract.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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.
+#
+
+"""
+bgp_set_local-preference_add_subtract.py:
+Test if we can add/subtract the value to/from an existing
+LOCAL_PREF in route-maps.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_set_local_preference():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
+ },
+ "192.168.255.3": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 3}},
+ },
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_local_preference(router):
+ output = json.loads(router.vtysh_cmd("show bgp ipv4 unicast json"))
+ expected = {
+ "routes": {
+ "10.10.10.2/32": [{"locPrf": 160}],
+ "10.10.10.3/32": [{"locPrf": 40}],
+ "172.16.255.254/32": [
+ {"locPrf": 50, "nexthops": [{"ip": "192.168.255.3"}]},
+ {"locPrf": 150, "nexthops": [{"ip": "192.168.255.2"}]},
+ ],
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+
+ assert result is None, 'Failed to see BGP convergence in "{}"'.format(router)
+
+ test_func = functools.partial(_bgp_check_local_preference, router)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+
+ assert result is None, 'Failed to see applied BGP local-preference in "{}"'.format(
+ router
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf
new file mode 100644
index 0000000..b598666
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/bgpd.conf
@@ -0,0 +1,12 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65001
+ timers bgp 3 9
+ bgp router-id 192.168.100.10
+ neighbor 192.168.100.20 remote-as 65001
+ neighbor 192.168.100.20 update-source 192.168.100.10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf
new file mode 100644
index 0000000..4aff57a
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf
new file mode 100644
index 0000000..4a85798
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce1/zebra.conf
@@ -0,0 +1,19 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface ce1-eth0
+ ip address 192.168.100.10/24
+ ipv6 address 2000:1:1:100::10/64
+!
+!
+interface lo
+ ip address 10.5.5.5/32
+ ipv6 address 2000:5:5:5::5/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf
new file mode 100644
index 0000000..e388ccb
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/bgpd.conf
@@ -0,0 +1,12 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65001
+ bgp router-id 192.168.200.10
+ timers bgp 3 9
+ neighbor 192.168.200.20 remote-as 65001
+ neighbor 192.168.200.20 update-source 192.168.200.10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf
new file mode 100644
index 0000000..29c2041
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.6.6.6:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf
new file mode 100644
index 0000000..5e0aa5d
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce2/zebra.conf
@@ -0,0 +1,19 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface ce2-eth0
+ ip address 192.168.200.10/24
+ ipv6 address 2000:1:1:200::10/64
+!
+!
+interface lo
+ ip address 10.6.6.6/32
+ ipv6 address 2000:6:6:6::6/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf
new file mode 100644
index 0000000..e388ccb
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/bgpd.conf
@@ -0,0 +1,12 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65001
+ bgp router-id 192.168.200.10
+ timers bgp 3 9
+ neighbor 192.168.200.20 remote-as 65001
+ neighbor 192.168.200.20 update-source 192.168.200.10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf
new file mode 100644
index 0000000..4aff57a
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf
new file mode 100644
index 0000000..fabc11e
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce3/zebra.conf
@@ -0,0 +1,19 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface ce3-eth0
+ ip address 192.168.200.10/24
+ ipv6 address 2000:1:1:200::10/64
+!
+!
+interface lo
+ ip address 10.7.7.7/32
+ ipv6 address 2000:7:7:7::7/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf
new file mode 100644
index 0000000..e388ccb
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/bgpd.conf
@@ -0,0 +1,12 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65001
+ bgp router-id 192.168.200.10
+ timers bgp 3 9
+ neighbor 192.168.200.20 remote-as 65001
+ neighbor 192.168.200.20 update-source 192.168.200.10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf
new file mode 100644
index 0000000..4aff57a
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.5.5.5:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf
new file mode 100644
index 0000000..e369f41
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/ce4/zebra.conf
@@ -0,0 +1,19 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface ce4-eth0
+ ip address 192.168.34.10/24
+ ipv6 address 2000:1:1:300::10/64
+!
+!
+interface lo
+ ip address 10.8.8.8/32
+ ipv6 address 2000:8:8:8::8/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf
new file mode 100644
index 0000000..098e55d
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/bgpd.conf
@@ -0,0 +1,48 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65000
+ bgp router-id 10.1.1.1
+ neighbor 10.4.4.4 remote-as 65000
+ neighbor 10.4.4.4 update-source 10.1.1.1
+ neighbor 10.4.4.4 timers connect 10
+ !
+ address-family ipv4 vpn
+ neighbor 10.4.4.4 activate
+ exit-address-family
+
+!
+router bgp 65001 vrf VRF-a
+ bgp router-id 192.168.100.20
+ timers bgp 3 9
+ neighbor 192.168.100.10 remote-as 65001
+ neighbor 192.168.100.10 update-source 192.168.100.20
+ neighbor 192.168.200.10 remote-as 65001
+ neighbor 192.168.200.10 update-source 192.168.200.20
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute isis
+ label vpn export 1111
+ rd vpn export 10:1
+ rt vpn both 1:1
+ export vpn
+ import vpn
+ exit-address-family
+
+router bgp 65002 vrf VRF-b
+ bgp router-id 192.168.10.20
+ timers bgp 3 9
+ neighbor 192.168.10.10 remote-as 65003
+ neighbor 192.168.10.10 update-source 192.168.10.20
+!
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute isis
+ label vpn export 6666
+ rd vpn export 10:2
+ rt vpn both 1:2
+ export vpn
+ import vpn
+ exit-address-family
+
+agentx
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf
new file mode 100644
index 0000000..1a148f0
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/isisd.conf
@@ -0,0 +1,46 @@
+log stdout debugging
+!
+! debug isis route-events
+! debug isis events
+!
+interface r1-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf
new file mode 100644
index 0000000..2ada53c
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/snmpd.conf
@@ -0,0 +1,20 @@
+agentAddress udp:10.1.1.1:161
+
+com2sec public 10.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+noRangeCheck yes
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf
new file mode 100644
index 0000000..7228ae6
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r1/zebra.conf
@@ -0,0 +1,33 @@
+log file zebra.log
+!
+interface r1-eth0
+ ip address 192.168.12.12/24
+ ipv6 address 2000:1:1:12::12/64
+!
+interface r1-eth1
+ ip address 192.168.13.13/24
+ ipv6 address 2000:1:1:13::13/64
+!
+interface r1-eth2
+ ip address 192.168.14.14/24
+ ipv6 address 2000:1:1:14::14/64
+!
+interface r1-eth3 vrf VRF-a
+ ip address 192.168.100.20/24
+ ipv6 address 2000:1:1:100::20/64
+!
+interface r1-eth4 vrf VRF-a
+ ip address 192.168.200.20/24
+ ipv6 address 2000:1:1:200::20/64
+!
+interface r1-eth5 vrf VRF-b
+ ip address 192.168.300.20/24
+ ipv6 address 2000:1:1:300::20/64
+
+interface lo
+ ip address 10.1.1.1/32
+ ipv6 address 2000:1:1:1::1/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf
new file mode 100644
index 0000000..396797d
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/isisd.conf
@@ -0,0 +1,37 @@
+log stdout debugging
+!
+! debug isis route-events
+! debug isis events
+!
+interface r2-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r2-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf
new file mode 100644
index 0000000..3db1ab7
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.2.2.2:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf
new file mode 100644
index 0000000..4fec8af
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r2/zebra.conf
@@ -0,0 +1,24 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface r2-eth0
+ ip address 192.168.12.21/24
+ ipv6 address 2000:1:1:12::21/64
+!
+interface r2-eth1
+ ip address 192.168.23.23/24
+ ipv6 address 2000:1:1:23::23/64
+!
+!
+interface lo
+ ip address 10.2.2.2/32
+ ipv6 address 2000:2:2:2::2/128
+!
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf
new file mode 100644
index 0000000..9e52fb6
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/isisd.conf
@@ -0,0 +1,45 @@
+log stdout debugging
+!
+! debug isis route-events
+! debug isis events
+!
+interface r3-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r3-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r3-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf
new file mode 100644
index 0000000..494df81
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.3.3.3:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf
new file mode 100644
index 0000000..e433995
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r3/zebra.conf
@@ -0,0 +1,27 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface r3-eth0
+ ip address 192.168.13.31/24
+ ipv6 address 2000:1:1:13::31/64
+!
+interface r3-eth1
+ ip address 192.168.23.32/24
+ ipv6 address 2000:1:1:23::32/64
+!
+interface r3-eth2
+ ip address 192.168.34.34/24
+ ipv6 address 2000:1:1:34::34/64
+!
+!
+interface lo
+ ip address 10.3.3.3/32
+ ipv6 address 2000:3:3:3::3/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf
new file mode 100644
index 0000000..2a834c7
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/bgpd.conf
@@ -0,0 +1,43 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 65000
+ bgp router-id 10.4.4.4
+ timers bgp 3 9
+ neighbor 10.1.1.1 remote-as 65000
+ neighbor 10.1.1.1 update-source 10.4.4.4
+ neighbor 10.1.1.1 timers connect 10
+ !
+ address-family ipv4 vpn
+ neighbor 10.1.1.1 activate
+ exit-address-family
+!
+
+ address-family ipv6 vpn
+ neighbor 10.1.1.1 activate
+ exit-address-family
+!
+router bgp 65001 vrf VRF-a
+ bgp router-id 192.168.200.20
+ timers bgp 3 9
+ neighbor 192.168.200.10 remote-as 65001
+ neighbor 192.168.200.10 update-source 192.168.200.20
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute isis
+ label vpn export 1111
+ rd vpn export 10:3
+ rt vpn both 1:1
+ export vpn
+ import vpn
+ exit-address-family
+
+ address-family ipv6 unicast
+ redistribute connected
+ redistribute isis
+ label vpn export 1111
+ rd vpn export 10:3
+ rt vpn both 1:2
+ export vpn
+ import vpn
+ exit-address-family
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf
new file mode 100644
index 0000000..8de2cf0
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/isisd.conf
@@ -0,0 +1,36 @@
+log stdout debugging
+!
+! debug isis route-events
+! debug isis events
+!
+interface r4-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r4-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf
new file mode 100644
index 0000000..f380960
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:10.4.4.4:161
+
+com2sec public localhost public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf b/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf
new file mode 100644
index 0000000..14580e5
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/r4/zebra.conf
@@ -0,0 +1,27 @@
+log file /tmp/zebra.log
+log stdout
+!
+! debug zebra events
+! debug zebra dplane
+!
+!
+interface r4-eth0
+ ip address 192.168.14.41/24
+ ipv6 address 2000:1:1:14::41/64
+!
+interface r4-eth1
+ ip address 192.168.34.43/24
+ ipv6 address 2000:1:1:34::43/64
+!
+interface r4-eth2 vrf aaa
+ ip address 192.168.200.20/24
+ ipv6 address 2000:1:1:200::20/64
+!
+!
+interface lo
+ ip address 10.4.4.4/32
+ ipv6 address 2000:4:4:4::4/128
+!
+!
+line vty
+!
diff --git a/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py
new file mode 100755
index 0000000..bc53dfb
--- /dev/null
+++ b/tests/topotests/bgp_snmp_mplsl3vpn/test_bgp_snmp_mplsvpn.py
@@ -0,0 +1,751 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_snmp_mplsl3vpn.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_snmp_mplsl3vpn.py: Test mplsL3Vpn MIB [RFC4382].
+"""
+
+import os
+import sys
+from time import sleep
+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.topogen import Topogen, TopoRouter, get_topogen
+from lib.snmptest import SnmpTester
+from lib import topotest
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.snmp]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to define allocation and relationship
+ # between routers, switches and hosts.
+ #
+ #
+ # Create routers
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("r3")
+ tgen.add_router("r4")
+ tgen.add_router("ce1")
+ tgen.add_router("ce2")
+ tgen.add_router("ce3")
+ tgen.add_router("ce4")
+
+ # r1-r2
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # r1-r3
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ # r1-r4
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r4"])
+
+ # r1-ce1
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["ce1"])
+
+ # r1-ce3
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["ce3"])
+
+ # r1-ce4
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["ce4"])
+
+ # r1-dangling
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["r1"])
+
+ # r2-r3
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # r3-r4
+ switch = tgen.add_switch("s9")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # r4-ce2
+ switch = tgen.add_switch("s10")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["ce2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # skip tests is SNMP not installed
+ snmpd = os.system("which snmpd")
+ if snmpd:
+ error_msg = "SNMP not installed - skipping"
+ pytest.skip(error_msg)
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+
+ # setup VRF-a in r1
+ r1.run("ip link add VRF-a type vrf table 1001")
+ r1.run("ip link set up dev VRF-a")
+ r1.run("ip link add VRF-b type vrf table 1002")
+ r1.run("ip link set up dev VRF-b")
+ r4.run("ip link add VRF-a type vrf table 1001")
+ r4.run("ip link set up dev VRF-a")
+
+ # enslave vrf interfaces
+ r1.run("ip link set r1-eth3 master VRF-a")
+ r1.run("ip link set r1-eth4 master VRF-a")
+ r1.run("ip link set r1-eth5 master VRF-b")
+ r4.run("ip link set r4-eth1 master VRF-a")
+
+ r1.run("sysctl -w net.ipv4.ip_forward=1")
+ r2.run("sysctl -w net.ipv4.ip_forward=1")
+ r3.run("sysctl -w net.ipv4.ip_forward=1")
+ r4.run("sysctl -w net.ipv4.ip_forward=1")
+ r1.run("sysctl -w net.mpls.conf.r1-eth0.input=1")
+ r1.run("sysctl -w net.mpls.conf.r1-eth1.input=1")
+ r1.run("sysctl -w net.mpls.conf.r1-eth2.input=1")
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, "{}/bgpd.conf".format(rname)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP,
+ os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap",
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+# SNMP utilities - maybe move to lib
+def snmp_uint32_to_oid(val):
+ oid1 = int(val / 16777216) % 256
+ oid2 = int(val / 65536) % 256
+ oid3 = int(val / 256) % 256
+ oid4 = int(val) % 256
+ return "%(oid1)s.%(oid2)s.%(oid3)s.%(oid4)s" % locals()
+
+
+def snmp_oid_to_uint32(oid):
+ values = oid.split(".")
+ return (
+ (int(values[0]) * 16777216)
+ + (int(values[1]) * 65536)
+ + (int(values[2]) * 256)
+ + int(values[3])
+ )
+
+
+def snmp_str_to_oid(str):
+ out_oid = ""
+ for char in str:
+ out_oid += "{}.".format(ord(char))
+ return out_oid.rstrip(".")
+
+
+def snmp_oid_to_str(oid):
+ out_str = ""
+ oids = oid.split(".")
+ for char in oids:
+ out_str += "{}".format(chr(int(char)))
+ return out_str
+
+
+def snmp_rte_oid(vrf, dtype, dest, plen, policy, ntype, nhop=0):
+ oid_1 = snmp_str_to_oid(vrf)
+ oid_2 = dtype
+ oid_3 = dest
+ oid_4 = plen
+ oid_5 = "0.{}".format(policy)
+ oid_6 = ntype
+ if ntype == 0:
+ oid_7 = ""
+ else:
+ oid_7 = ".{}".format(nhop)
+
+ return "{}.{}.{}.{}.{}.{}{}".format(oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7)
+
+
+def test_pe1_converge_evpn():
+ "Wait for protocol convergence"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+
+ def _convergence():
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ return r1_snmp.test_oid("bgpVersion", "10")
+
+ _, result = topotest.run_and_expect(_convergence, True, count=20, wait=1)
+ assertmsg = "BGP SNMP does not seem to be running"
+ assert result, assertmsg
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+ count = 0
+ passed = False
+ while count < 125:
+ if r1_snmp.test_oid_walk("bgpPeerLocalAddr.10.4.4.4", ["10.1.1.1"]):
+ passed = True
+ break
+ count += 1
+ sleep(1)
+ # tgen.mininet_cli()
+ assertmsg = "BGP Peer 10.4.4.4 did not connect"
+ assert passed, assertmsg
+
+
+interfaces_up_test = {
+ "mplsL3VpnConfiguredVrfs": "2",
+ "mplsL3VpnActiveVrfs": "2",
+ "mplsL3VpnConnectedInterfaces": "3",
+ "mplsL3VpnNotificationEnable": "true(1)",
+ "mplsL3VpnVrfConfMaxPossRts": "0",
+ "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds",
+ "mplsL3VpnIlllblRcvThrsh": "0",
+}
+
+interfaces_down_test = {
+ "mplsL3VpnConfiguredVrfs": "2",
+ "mplsL3VpnActiveVrfs": "1",
+ "mplsL3VpnConnectedInterfaces": "3",
+ "mplsL3VpnNotificationEnable": "true(1)",
+ "mplsL3VpnVrfConfMaxPossRts": "0",
+ "mplsL3VpnVrfConfRteMxThrshTime": "0 seconds",
+ "mplsL3VpnIlllblRcvThrsh": "0",
+}
+
+
+def test_r1_mplsvpn_scalars():
+ "check scalar values"
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ for item in interfaces_up_test.keys():
+ assertmsg = "{} should be {}: value {}".format(
+ item, interfaces_up_test[item], r1_snmp.get_next(item)
+ )
+ assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg
+
+
+def test_r1_mplsvpn_scalars_interface():
+ "check scalar interface changing values"
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown")
+ r1.vtysh_cmd("conf t\ninterface r1-eth4\nshutdown")
+
+ for item in interfaces_up_test.keys():
+ assertmsg = "{} should be {}: value {}".format(
+ item, interfaces_down_test[item], r1_snmp.get_next(item)
+ )
+ assert r1_snmp.test_oid(item, interfaces_down_test[item]), assertmsg
+
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown")
+ r1.vtysh_cmd("conf t\ninterface r1-eth4\nno shutdown")
+
+ for item in interfaces_up_test.keys():
+ assertmsg = "{} should be {}: value {}".format(
+ item, interfaces_up_test[item], r1_snmp.get_next(item)
+ )
+ assert r1_snmp.test_oid(item, interfaces_up_test[item]), assertmsg
+
+
+def router_interface_get_ifindex(router, interface):
+ ifindex = 0
+ r_int_output = router.vtysh_cmd(
+ "show interface {}-{}".format(router.name, interface)
+ )
+ int_lines = r_int_output.splitlines()
+ for line in int_lines:
+ line_items = line.lstrip().split(" ")
+ if "index" in line_items[0]:
+ ifindex = line_items[1]
+ return ifindex
+
+
+def generate_vrf_ifindex_oid(vrf, ifindex):
+
+ intoid = snmp_uint32_to_oid(int(ifindex))
+ vrfoid = snmp_str_to_oid(vrf)
+ oid = "{}.{}".format(vrfoid, intoid)
+
+ return oid
+
+
+def generate_vrf_index_type_oid(vrf, index, type):
+ vrfoid = snmp_str_to_oid(vrf)
+ intoid = snmp_uint32_to_oid(int(index))
+ oid = "{}.{}.{}".format(vrfoid, intoid, type)
+
+ return oid
+
+
+iftable_up_test = {
+ "mplsL3VpnIfVpnClassification": ["enterprise(2)", "enterprise(2)", "enterprise(2)"],
+ "mplsL3VpnIfConfStorageType": ["volatile(2)", "volatile(2)", "volatile(2)"],
+ "mplsL3VpnIfConfRowStatus": ["active(1)", "active(1)", "active(1)"],
+}
+
+
+def get_timetick_val(time):
+ return int(time.split(" ")[0].lstrip("(").rstrip(")"))
+
+
+def test_r1_mplsvpn_IfTable():
+ "mplsL3VpnIf table values"
+
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ # tgen.mininet_cli()
+ eth3_ifindex = router_interface_get_ifindex(r1, "eth3")
+ eth4_ifindex = router_interface_get_ifindex(r1, "eth4")
+ eth5_ifindex = router_interface_get_ifindex(r1, "eth5")
+
+ # get ifindex and make sure the oid is correct
+
+ oids = []
+ # generate oid
+ oids.append(generate_vrf_ifindex_oid("VRF-a", eth3_ifindex))
+ oids.append(generate_vrf_ifindex_oid("VRF-a", eth4_ifindex))
+ oids.append(generate_vrf_ifindex_oid("VRF-b", eth5_ifindex))
+
+ for item in iftable_up_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, iftable_up_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg
+
+ # an inactive vrf should not affect these values
+ r1.cmd("ip link set r1-eth5 down")
+
+ for item in iftable_up_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, iftable_up_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, iftable_up_test[item], oids), assertmsg
+
+ r1.cmd("ip link set r1-eth5 up")
+
+
+vrftable_test = {
+ "mplsL3VpnVrfDescription": ["VRF-a", "VRF-b"],
+ "mplsL3VpnVrfRD": ['"10:1"', '"10:2"'],
+ "mplsL3VpnVrfOperStatus": ["up(1)", "up(1)"],
+ "mplsL3VpnVrfActiveInterfaces": ["2", "1"],
+ "mplsL3VpnVrfAssociatedInterfaces": ["2", "1"],
+ "mplsL3VpnVrfConfMidRteThresh": ["0", "0"],
+ "mplsL3VpnVrfConfHighRteThresh": ["0", "0"],
+ "mplsL3VpnVrfConfMaxRoutes": ["0", "0"],
+ "mplsL3VpnVrfConfRowStatus": ["active(1)", "active(1)"],
+ "mplsL3VpnVrfConfAdminStatus": ["up(1)", "up(1)"],
+ "mplsL3VpnVrfConfStorageType": ["volatile(2)", "volatile(2)"],
+}
+
+
+def test_r1_mplsvpn_VrfTable():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ # tgen.mininet_cli()
+
+ oids = []
+
+ oids.append(snmp_str_to_oid("VRF-a"))
+ oids.append(snmp_str_to_oid("VRF-b"))
+
+ # check items
+ for item in vrftable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, vrftable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, vrftable_test[item], oids), assertmsg
+
+ # check timetick set and stable
+ ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a")))
+ ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b")))
+ ts_val_a1 = get_timetick_val(ts_a)
+ ts_val_b1 = get_timetick_val(ts_b)
+ ts_a = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-a")))
+ ts_b = r1_snmp.get("mplsL3VpnVrfCreationTime.{}".format(snmp_str_to_oid("VRF-b")))
+ ts_val_a2 = get_timetick_val(ts_a)
+ ts_val_b2 = get_timetick_val(ts_b)
+
+ assertmsg = "timestamp values for VRF-a do not match {} {}".format(
+ ts_val_a1, ts_val_a2
+ )
+ assert ts_val_a1 == ts_val_a2, assertmsg
+ assertmsg = "timestamp values for VRF-b do not match {} {}".format(
+ ts_val_b1, ts_val_b2
+ )
+ assert ts_val_b1 == ts_val_b2, assertmsg
+
+ # take Last changed time, fiddle with active interfaces, ensure
+ # time changes and active interfaces change
+ ts_last = r1_snmp.get(
+ "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ ts_val_last_1 = get_timetick_val(ts_last)
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nshutdown")
+ active_int = r1_snmp.get(
+ "mplsL3VpnVrfActiveInterfaces.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ assertmsg = "mplsL3VpnVrfActiveInterfaces incorrect should be 1 value {}".format(
+ active_int
+ )
+ assert active_int == "1", assertmsg
+
+ ts_last = r1_snmp.get(
+ "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ ts_val_last_2 = get_timetick_val(ts_last)
+ assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change"
+ assert ts_val_last_2 > ts_val_last_1, assertmsg
+ r1.vtysh_cmd("conf t\ninterface r1-eth3\nno shutdown")
+
+ # take Last changed time, fiddle with associated interfaces, ensure
+ # time changes and active interfaces change
+ ts_last = r1_snmp.get(
+ "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ ts_val_last_1 = get_timetick_val(ts_last)
+ r1.cmd("ip link set r1-eth6 master VRF-a")
+ r1.cmd("ip link set r1-eth6 up")
+
+ associated_int = r1_snmp.get(
+ "mplsL3VpnVrfAssociatedInterfaces.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ assertmsg = (
+ "mplsL3VpnVrfAssociatedInterfaces incorrect should be 3 value {}".format(
+ associated_int
+ )
+ )
+
+ assert associated_int == "3", assertmsg
+ ts_last = r1_snmp.get(
+ "mplsL3VpnVrfConfLastChanged.{}".format(snmp_str_to_oid("VRF-a"))
+ )
+ ts_val_last_2 = get_timetick_val(ts_last)
+ assertmsg = "mplsL3VpnVrfConfLastChanged does not update on interface change"
+ assert ts_val_last_2 > ts_val_last_1, assertmsg
+ r1.cmd("ip link del r1-eth6 master VRF-a")
+ r1.cmd("ip link set r1-eth6 down")
+
+
+rt_table_test = {
+ "mplsL3VpnVrfRT": ['"1:1"', '"1:2"'],
+ "mplsL3VpnVrfRTDescr": ["RT both for VRF VRF-a", "RT both for VRF VRF-b"],
+ "mplsL3VpnVrfRTRowStatus": ["active(1)", "active(1)"],
+ "mplsL3VpnVrfRTStorageType": ["volatile(2)", "volatile(2)"],
+}
+
+
+def test_r1_mplsvpn_VrfRT_table():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_vrf_index_type_oid("VRF-a", 1, 3))
+ oids.append(generate_vrf_index_type_oid("VRF-b", 1, 3))
+
+ # check items
+ for item in rt_table_test.keys():
+ print(item)
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, rt_table_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, rt_table_test[item], oids), assertmsg
+
+
+def test_r1_mplsvpn_perf_table():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ # tgen.mininet_cli()
+ oid_a = snmp_str_to_oid("VRF-a")
+ oid_b = snmp_str_to_oid("VRF-b")
+
+ # poll for 10 seconds for routes to appear
+ count = 0
+ passed = False
+ while count < 60:
+ if r1_snmp.test_oid_walk(
+ "mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a), ["7"]
+ ):
+ passed = True
+ break
+ count += 1
+ sleep(1)
+ # tgen.mininet_cli()
+ assertmsg = "mplsL3VpnVrfPerfCurrNumRoutes shouold be 7 got {}".format(
+ r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a))
+ )
+ assert passed, assertmsg
+ curr_a = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_a)))
+ del_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_a)))
+ add_a = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_a)))
+
+ assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format(
+ curr_a, add_a, del_a
+ )
+ assert curr_a == (add_a - del_a), assertmsg
+ curr_b = int(r1_snmp.get("mplsL3VpnVrfPerfCurrNumRoutes.{}".format(oid_b)))
+ del_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesDeleted.{}".format(oid_b)))
+ add_b = int(r1_snmp.get("mplsL3VpnVrfPerfRoutesAdded.{}".format(oid_b)))
+ assertmsg = "FAIL curr{} does not equal added{} - deleted {}".format(
+ curr_b, add_b, del_b
+ )
+ assert curr_b == (add_b - del_b), assertmsg
+
+
+rte_table_test = {
+ "mplsL3VpnVrfRteInetCidrDestType": [
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ ],
+ "mplsL3VpnVrfRteInetCidrDest": [
+ "0A 05 05 05",
+ "0A 07 07 07",
+ "C0 A8 22 00",
+ "C0 A8 64 00",
+ "C0 A8 64 00",
+ "C0 A8 C8 00",
+ "C0 A8 C8 00",
+ ],
+ "mplsL3VpnVrfRteInetCidrPfxLen": ["32", "32", "24", "24", "24", "24", "24"],
+ "mplsL3VpnVrfRteInetCidrNHopType": [
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "ipv4(1)",
+ "unknown(0)",
+ "ipv4(1)",
+ "unknown(0)",
+ ],
+ "mplsL3VpnVrfRteInetCidrNextHop": [
+ "C0 A8 64 0A",
+ "C0 A8 C8 0A",
+ "0A 04 04 04",
+ "C0 A8 64 0A",
+ '""',
+ "C0 A8 C8 0A",
+ '""',
+ ],
+ "mplsL3VpnVrfRteInetCidrType": [
+ "local(3)",
+ "local(3)",
+ "remote(4)",
+ "local(3)",
+ "other(1)",
+ "local(3)",
+ "other(1)",
+ ],
+ "mplsL3VpnVrfRteInetCidrProto": [
+ "bgp(14)",
+ "bgp(14)",
+ "bgp(14)",
+ "bgp(14)",
+ "local(2)",
+ "bgp(14)",
+ "local(2)",
+ ],
+ "mplsL3VpnVrfRteInetCidrNextHopAS": [
+ "65001",
+ "65001",
+ "0",
+ "65001",
+ "0",
+ "65001",
+ "0",
+ ],
+ "mplsL3VpnVrfRteInetCidrMetric1": ["0", "0", "20", "0", "0", "0", "0"],
+ "mplsL3VpnVrfRteInetCidrMetric2": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"],
+ "mplsL3VpnVrfRteInetCidrMetric3": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"],
+ "mplsL3VpnVrfRteInetCidrMetric4": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"],
+ "mplsL3VpnVrfRteInetCidrMetric5": ["-1", "-1", "-1", "-1", "-1", "-1", "-1"],
+ "mplsL3VpnVrfRteXCPointer": ["00", "00", "00", "00", "00", "00", "00"],
+ "mplsL3VpnVrfRteInetCidrStatus": [
+ "active(1)",
+ "active(1)",
+ "active(1)",
+ "active(1)",
+ "active(1)",
+ "active(1)",
+ "active(1)",
+ ],
+}
+
+
+def test_r1_mplsvpn_rte_table():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+
+ r1_snmp = SnmpTester(r1, "10.1.1.1", "public", "2c")
+
+ # tgen.mininet_cli()
+ oid_1 = snmp_rte_oid("VRF-a", 1, "10.5.5.5", 32, 0, 1, "192.168.100.10")
+ oid_2 = snmp_rte_oid("VRF-a", 1, "10.7.7.7", 32, 0, 1, "192.168.200.10")
+ oid_3 = snmp_rte_oid("VRF-a", 1, "192.168.34.0", 24, 0, 1, "10.4.4.4")
+ oid_4 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 1, "192.168.100.10")
+ oid_4_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 1, "192.168.100.10")
+ oid_5 = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 0, 0)
+ oid_5_a = snmp_rte_oid("VRF-a", 1, "192.168.100.0", 24, 1, 0)
+ oid_6 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 1, "192.168.200.10")
+ oid_6_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 1, "192.168.200.10")
+ oid_7 = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 0, 0)
+ oid_7_a = snmp_rte_oid("VRF-a", 1, "192.168.200.0", 24, 1, 0)
+
+ oid_lists = [
+ [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7],
+ [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7],
+ [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a],
+ [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a],
+ [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6, oid_7],
+ [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6, oid_7],
+ [oid_1, oid_2, oid_3, oid_4, oid_5, oid_6_a, oid_7_a],
+ [oid_1, oid_2, oid_3, oid_4_a, oid_5_a, oid_6_a, oid_7_a],
+ ]
+
+ # check items
+
+ passed = False
+ for oid_list in oid_lists:
+ passed = True
+ for item in rte_table_test.keys():
+ print(item)
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, rte_table_test[item], oid_list, r1_snmp.walk(item)
+ )
+ if not r1_snmp.test_oid_walk(item, rte_table_test[item], oid_list):
+ passed = False
+ break
+ print(
+ "{} should be {} oids {} full dict {}:".format(
+ item, rte_table_test[item], oid_list, r1_snmp.walk(item)
+ )
+ )
+ if passed:
+ break
+ # generate ifindex row grabbing ifindices from vtysh
+ if passed:
+ ifindex_row = [
+ router_interface_get_ifindex(r1, "eth3"),
+ router_interface_get_ifindex(r1, "eth4"),
+ router_interface_get_ifindex(r1, "eth2"),
+ router_interface_get_ifindex(r1, "eth3"),
+ "0",
+ router_interface_get_ifindex(r1, "eth4"),
+ "0",
+ ]
+ if not r1_snmp.test_oid_walk(
+ "mplsL3VpnVrfRteInetCidrIfIndex", ifindex_row, oid_list
+ ):
+ passed = False
+
+ print("passed {}".format(passed))
+ assert passed, assertmsg
+
+
+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_soo/__init__.py b/tests/topotests/bgp_soo/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_soo/__init__.py
diff --git a/tests/topotests/bgp_soo/cpe1/bgpd.conf b/tests/topotests/bgp_soo/cpe1/bgpd.conf
new file mode 100644
index 0000000..a8984d4
--- /dev/null
+++ b/tests/topotests/bgp_soo/cpe1/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 1 3
+ neighbor 192.168.1.2 timers connect 1
+ neighbor 10.0.0.2 remote-as internal
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_soo/cpe1/zebra.conf b/tests/topotests/bgp_soo/cpe1/zebra.conf
new file mode 100644
index 0000000..669cb91
--- /dev/null
+++ b/tests/topotests/bgp_soo/cpe1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 172.16.255.1/32
+!
+interface cpe1-eth0
+ ip address 192.168.1.1/24
+!
+interface cpe1-eth1
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_soo/cpe2/bgpd.conf b/tests/topotests/bgp_soo/cpe2/bgpd.conf
new file mode 100644
index 0000000..19f7a24
--- /dev/null
+++ b/tests/topotests/bgp_soo/cpe2/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.2 remote-as external
+ neighbor 192.168.2.2 timers 1 3
+ neighbor 192.168.2.2 timers connect 1
+ neighbor 10.0.0.1 remote-as internal
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_soo/cpe2/zebra.conf b/tests/topotests/bgp_soo/cpe2/zebra.conf
new file mode 100644
index 0000000..52f36c0
--- /dev/null
+++ b/tests/topotests/bgp_soo/cpe2/zebra.conf
@@ -0,0 +1,9 @@
+!
+interface cpe2-eth0
+ ip address 192.168.2.1/24
+!
+interface cpe2-eth1
+ ip address 10.0.0.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_soo/pe1/bgpd.conf b/tests/topotests/bgp_soo/pe1/bgpd.conf
new file mode 100644
index 0000000..04a6857
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe1/bgpd.conf
@@ -0,0 +1,27 @@
+router bgp 65001
+ bgp router-id 10.10.10.10
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 10.10.10.20 remote-as internal
+ neighbor 10.10.10.20 update-source 10.10.10.10
+ address-family ipv4 vpn
+ neighbor 10.10.10.20 activate
+ exit-address-family
+!
+router bgp 65001 vrf RED
+ bgp router-id 192.168.1.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 1 3
+ neighbor 192.168.1.1 timers connect 1
+ address-family ipv4 unicast
+ neighbor 192.168.1.1 as-override
+ neighbor 192.168.1.1 soo 65000:1
+ label vpn export 1111
+ rd vpn export 192.168.1.2:2
+ rt vpn import 192.168.2.2:2 192.168.1.2:2
+ rt vpn export 192.168.1.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_soo/pe1/ldpd.conf b/tests/topotests/bgp_soo/pe1/ldpd.conf
new file mode 100644
index 0000000..fb40f06
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe1/ldpd.conf
@@ -0,0 +1,10 @@
+mpls ldp
+ router-id 10.10.10.10
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.10
+ !
+ interface pe1-eth1
+ !
+ !
+!
diff --git a/tests/topotests/bgp_soo/pe1/ospfd.conf b/tests/topotests/bgp_soo/pe1/ospfd.conf
new file mode 100644
index 0000000..34f0899
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe1/ospfd.conf
@@ -0,0 +1,7 @@
+interface pe1-eth1
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+!
+router ospf
+ router-id 10.10.10.10
+ network 0.0.0.0/0 area 0
diff --git a/tests/topotests/bgp_soo/pe1/zebra.conf b/tests/topotests/bgp_soo/pe1/zebra.conf
new file mode 100644
index 0000000..cc8ff19
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe1/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 10.10.10.10/32
+!
+interface pe1-eth0 vrf RED
+ ip address 192.168.1.2/24
+!
+interface pe1-eth1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_soo/pe2/bgpd.conf b/tests/topotests/bgp_soo/pe2/bgpd.conf
new file mode 100644
index 0000000..efebc02
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe2/bgpd.conf
@@ -0,0 +1,31 @@
+router bgp 65001
+ bgp router-id 10.10.10.20
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 10.10.10.10 remote-as internal
+ neighbor 10.10.10.10 update-source 10.10.10.20
+ address-family ipv4 vpn
+ neighbor 10.10.10.10 activate
+ exit-address-family
+!
+router bgp 65001 vrf RED
+ bgp router-id 192.168.2.2
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 1 3
+ neighbor 192.168.2.1 timers connect 1
+ address-family ipv4 unicast
+ neighbor 192.168.2.1 as-override
+ neighbor 192.168.2.1 route-map cpe2-in in
+ label vpn export 2222
+ rd vpn export 192.168.2.2:2
+ rt vpn import 192.168.2.2:2 192.168.1.2:2
+ rt vpn export 192.168.2.2:2
+ export vpn
+ import vpn
+ exit-address-family
+!
+! To prefer internal MPLS route over eBGP
+route-map cpe2-in permit 10
+ set local-preference 50
+exit
diff --git a/tests/topotests/bgp_soo/pe2/ldpd.conf b/tests/topotests/bgp_soo/pe2/ldpd.conf
new file mode 100644
index 0000000..e2b5359
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe2/ldpd.conf
@@ -0,0 +1,10 @@
+mpls ldp
+ router-id 10.10.10.20
+ !
+ address-family ipv4
+ discovery transport-address 10.10.10.20
+ !
+ interface pe2-eth0
+ !
+ !
+!
diff --git a/tests/topotests/bgp_soo/pe2/ospfd.conf b/tests/topotests/bgp_soo/pe2/ospfd.conf
new file mode 100644
index 0000000..4c4b137
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe2/ospfd.conf
@@ -0,0 +1,7 @@
+interface pe2-eth0
+ ip ospf dead-interval 4
+ ip ospf hello-interval 1
+!
+router ospf
+ router-id 10.10.10.20
+ network 0.0.0.0/0 area 0
diff --git a/tests/topotests/bgp_soo/pe2/zebra.conf b/tests/topotests/bgp_soo/pe2/zebra.conf
new file mode 100644
index 0000000..8049a74
--- /dev/null
+++ b/tests/topotests/bgp_soo/pe2/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 10.10.10.20/32
+!
+interface pe2-eth1 vrf RED
+ ip address 192.168.2.2/24
+!
+interface pe2-eth0
+ ip address 10.0.1.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_soo/test_bgp_soo.py b/tests/topotests/bgp_soo/test_bgp_soo.py
new file mode 100644
index 0000000..e3a7334
--- /dev/null
+++ b/tests/topotests/bgp_soo/test_bgp_soo.py
@@ -0,0 +1,186 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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 if BGP SoO per neighbor works correctly. Routes having SoO
+extended community MUST be rejected if the neighbor is configured
+with soo (neighbor soo).
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("cpe1")
+ tgen.add_router("cpe2")
+ tgen.add_router("pe1")
+ tgen.add_router("pe2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["cpe1"])
+ switch.add_link(tgen.gears["pe1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["pe1"])
+ switch.add_link(tgen.gears["pe2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["pe2"])
+ switch.add_link(tgen.gears["cpe2"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["cpe2"])
+ switch.add_link(tgen.gears["cpe1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ pe1 = tgen.gears["pe1"]
+ pe2 = tgen.gears["pe2"]
+
+ pe1.run("ip link add RED type vrf table 1001")
+ pe1.run("ip link set up dev RED")
+ pe2.run("ip link add RED type vrf table 1001")
+ pe2.run("ip link set up dev RED")
+ pe1.run("ip link set pe1-eth0 master RED")
+ pe2.run("ip link set pe2-eth1 master RED")
+
+ pe1.run("sysctl -w net.ipv4.ip_forward=1")
+ pe2.run("sysctl -w net.ipv4.ip_forward=1")
+ pe1.run("sysctl -w net.mpls.conf.pe1-eth0.input=1")
+ pe2.run("sysctl -w net.mpls.conf.pe2-eth1.input=1")
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_soo():
+ tgen = get_topogen()
+
+ pe2 = tgen.gears["pe2"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_soo_unconfigured():
+ output = json.loads(
+ pe2.vtysh_cmd(
+ "show bgp vrf RED ipv4 unicast neighbors 192.168.2.1 advertised-routes json"
+ )
+ )
+ expected = {"advertisedRoutes": {"172.16.255.1/32": {"path": "65001"}}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_soo_unconfigured)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "Failed to see BGP convergence in pe2"
+
+ step("Configure SoO (65000:1) for PE2 -- CPE2 session")
+ pe2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001 vrf RED
+ address-family ipv4 unicast
+ neighbor 192.168.2.1 soo 65000:1
+ """
+ )
+
+ def _bgp_soo_configured():
+ output = json.loads(
+ pe2.vtysh_cmd(
+ "show bgp vrf RED ipv4 unicast neighbors 192.168.2.1 advertised-routes json"
+ )
+ )
+ expected = {"advertisedRoutes": {"172.16.255.1/32": None}}
+ return topotest.json_cmp(output, expected)
+
+ test_func = functools.partial(_bgp_soo_configured)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "SoO filtering does not work from pe2"
+
+ step("Configure SoO (65000:2) for PE2 -- CPE2 session")
+ pe2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001 vrf RED
+ address-family ipv4 unicast
+ neighbor 192.168.2.1 soo 65000:2
+ """
+ )
+
+ test_func = functools.partial(_bgp_soo_unconfigured)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "SoO filtering does not work from pe2"
+
+ step("Unconfigure SoO for PE2 -- CPE2 session")
+ pe2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001 vrf RED
+ address-family ipv4 unicast
+ no neighbor 192.168.2.1 soo
+ """
+ )
+
+ test_func = functools.partial(_bgp_soo_unconfigured)
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=0.5)
+ assert result is None, "SoO filtering does not work from pe2"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf
new file mode 100644
index 0000000..bcf5a04
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.1.254
+ipv6 route ::/0 2001:1::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf
new file mode 100644
index 0000000..0615cf9
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c11/zebra.conf
@@ -0,0 +1,6 @@
+hostname c11
+!
+interface eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:1::1/64
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf
new file mode 100644
index 0000000..bcf5a04
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.1.254
+ipv6 route ::/0 2001:1::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf
new file mode 100644
index 0000000..18985aa
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c12/zebra.conf
@@ -0,0 +1,6 @@
+hostname c12
+!
+interface eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:1::1/64
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf
new file mode 100644
index 0000000..608e600
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/staticd.conf
@@ -0,0 +1,4 @@
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf
new file mode 100644
index 0000000..b8b70ac
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c21/zebra.conf
@@ -0,0 +1,6 @@
+hostname c21
+!
+interface eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:2::1/64
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/bgpd.conf
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf
new file mode 100644
index 0000000..277aae9
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/staticd.conf
@@ -0,0 +1,5 @@
+
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf
new file mode 100644
index 0000000..cc764cc
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/c22/zebra.conf
@@ -0,0 +1,9 @@
+hostname c22
+!
+interface eth0
+ ip address 192.168.2.1/24
+ ipv6 address 2001:2::1/64
+!
+ip route 0.0.0.0/0 192.168.2.254
+ipv6 route ::/0 2001:2::ffff
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf
new file mode 100644
index 0000000..048702f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/bgpd.conf
@@ -0,0 +1,52 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8::2 remote-as 65002
+ neighbor 2001:db8::2 timers 3 10
+ neighbor 2001:db8::2 timers connect 1
+ neighbor 2001:db8::2 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8::2 activate
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 1
+ rd vpn export 65001:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf20
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 2
+ rd vpn export 65001:20
+ rt vpn both 0:20
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf
new file mode 100644
index 0000000..662856f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/staticd.conf
@@ -0,0 +1,3 @@
+!
+ipv6 route 2001:db8:2:2::/64 2001:db8::2
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf
new file mode 100644
index 0000000..066748b
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ipv6 address 2001:db8:1:1::1/128
+!
+interface eth0
+ ipv6 address 2001:db8::1/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.1.254/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.1.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:1:1::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf
new file mode 100644
index 0000000..33b9103
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/bgpd.conf
@@ -0,0 +1,52 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65002
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001:db8::1 remote-as 65001
+ neighbor 2001:db8::1 timers 3 10
+ neighbor 2001:db8::1 timers connect 1
+ neighbor 2001:db8::1 capability extended-nexthop
+ !
+ segment-routing srv6
+ locator default
+ !
+ address-family ipv4 vpn
+ neighbor 2001:db8::1 activate
+ exit-address-family
+ !
+!
+router bgp 65002 vrf vrf10
+ bgp router-id 192.0.2.2
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 1
+ rd vpn export 65002:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65002 vrf vrf20
+ bgp router-id 192.0.2.2
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ sid vpn export 2
+ rd vpn export 65002:20
+ rt vpn both 0:20
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf
new file mode 100644
index 0000000..a2f54b7
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/staticd.conf
@@ -0,0 +1,3 @@
+!
+ipv6 route 2001:db8:1:1::/64 2001:db8::1
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf
new file mode 100644
index 0000000..dc03389
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/r2/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ipv6 address 2001:db8:2:2::1/128
+!
+interface eth0
+ ipv6 address 2001:db8::2/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.2.254/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.2.254/24
+!
+segment-routing
+ srv6
+ locators
+ locator default
+ prefix 2001:db8:2:2::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py b/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py
new file mode 100755
index 0000000..6a75fb8
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_over_ipv6/test_bgp_srv6l3vpn_over_ipv6.py
@@ -0,0 +1,128 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+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 required_linux_kernel_version
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ tgen.add_router("c11")
+ tgen.add_router("c12")
+ tgen.add_router("c21")
+ tgen.add_router("c22")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c11"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r1"], tgen.gears["c12"], "eth2", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c21"], "eth1", "eth0")
+ tgen.add_link(tgen.gears["r2"], tgen.gears["c22"], "eth2", "eth0")
+
+
+def setup_module(mod):
+ result = required_linux_kernel_version("5.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for rname, router in tgen.routers().items():
+ router.load_config(TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)))
+ router.load_config(TopoRouter.RD_STATIC,
+ os.path.join(CWD, '{}/staticd.conf'.format(rname)))
+ router.load_config(TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname)))
+
+ tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r1"].run("ip link set vrf10 up")
+ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r1"].run("ip link set vrf20 up")
+ tgen.gears["r1"].run("ip link set eth1 master vrf10")
+ tgen.gears["r1"].run("ip link set eth2 master vrf20")
+
+ tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r2"].run("ip link set vrf10 up")
+ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r2"].run("ip link set vrf20 up")
+ tgen.gears["r2"].run("ip link set eth1 master vrf10")
+ tgen.gears["r2"].run("ip link set eth2 master vrf20")
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def check_ping4(name, dest_addr, expected):
+ def _check(name, dest_addr, match):
+ tgen = get_topogen()
+ output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr))
+ logger.info(output)
+ if match not in output:
+ return "ping fail"
+
+ match = ", {} packet loss".format("0%" if expected else "100%")
+ logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, dest_addr, match)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=1)
+ assert result is None, "Failed"
+
+
+def test_ping():
+ tgen = get_topogen()
+ logger.info(tgen.gears["c11"].run("ip route show"))
+ # tests for ipv4-vpn
+ check_ping4("c11", "192.168.2.1", True)
+ check_ping4("c12", "192.168.2.1", True)
+ check_ping4("c21", "192.168.1.1", True)
+ check_ping4("c22", "192.168.1.1", True)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf
new file mode 100644
index 0000000..3459796
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json
new file mode 100644
index 0000000..d19e315
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:1::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:1::/64": [
+ {
+ "prefix": "2001:1::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf
new file mode 100644
index 0000000..665808a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce1/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ipv6 address 2001:1::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:1::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf
new file mode 100644
index 0000000..8ed9978
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce2
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json
new file mode 100644
index 0000000..35ff14e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:2::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:2::/64": [
+ {
+ "prefix": "2001:2::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf
new file mode 100644
index 0000000..cc9b90a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce2/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ipv6 address 2001:2::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:2::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf
new file mode 100644
index 0000000..a85d970
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce3
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json
new file mode 100644
index 0000000..2f2931f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:3::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "prefix": "2001:3::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf
new file mode 100644
index 0000000..beca0b1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce3/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface eth0
+ ipv6 address 2001:3::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:3::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf
new file mode 100644
index 0000000..93fb32f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce4
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json
new file mode 100644
index 0000000..8a98768
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:4::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:4::/64": [
+ {
+ "prefix": "2001:4::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf
new file mode 100644
index 0000000..7b21074
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce4/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce4
+!
+interface eth0
+ ipv6 address 2001:4::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:4::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf
new file mode 100644
index 0000000..2ab6f2d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce5
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json
new file mode 100644
index 0000000..80ff52a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:5::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:5::/64": [
+ {
+ "prefix": "2001:5::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf
new file mode 100644
index 0000000..b5ad48e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce5/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce5
+!
+interface eth0
+ ipv6 address 2001:5::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:5::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf
new file mode 100644
index 0000000..e0b6540
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce6
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json
new file mode 100644
index 0000000..ace6136
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/ipv6_rib.json
@@ -0,0 +1,58 @@
+{
+ "::/0": [
+ {
+ "prefix": "::/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "2001:6::1",
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "prefix": "2001:6::/64",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf
new file mode 100644
index 0000000..7d19d98
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/ce6/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce6
+!
+interface eth0
+ ipv6 address 2001:6::2/64
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route ::/0 2001:6::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf
new file mode 100644
index 0000000..d113db1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/bgpd.conf
@@ -0,0 +1,66 @@
+frr defaults traditional
+!
+bgp send-extra-data zebra
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 1
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::2 remote-as 2
+ neighbor 2001::2 timers 3 10
+ neighbor 2001::2 timers connect 1
+ !
+ address-family ipv6 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 1 vrf vrf10
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 1:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 1 vrf vrf20
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 1:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json
new file mode 100644
index 0000000..25b7a86
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib.json
@@ -0,0 +1,170 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json
new file mode 100644
index 0000000..f2df9be
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_deleted.json
@@ -0,0 +1,160 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json
new file mode 100644
index 0000000..0fdd3d6
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vpnv6_rib_locator_recreated.json
@@ -0,0 +1,169 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json
new file mode 100644
index 0000000..141c1cb
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf10_rib.json
@@ -0,0 +1,86 @@
+{
+ "2001:1::/64": [
+ {
+ "prefix": "2001:1::/64",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:2::/64": [
+ {
+ "prefix": "2001:2::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:100::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "prefix": "2001:3::/64",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth2",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json
new file mode 100644
index 0000000..e209980
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/vrf20_rib.json
@@ -0,0 +1,92 @@
+{
+ "2001:4::/64": [
+ {
+ "prefix": "2001:4::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:200::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ],
+ "2001:5::/64": [
+ {
+ "prefix": "2001:5::/64",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth3",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "prefix": "2001:6::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:200::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf
new file mode 100644
index 0000000..8defa01
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r1/zebra.conf
@@ -0,0 +1,42 @@
+log file zebra.log
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+! debug zebra packet
+! debug zebra dplane
+! debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::1/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:1::1/64
+!
+interface eth2 vrf vrf10
+ ipv6 address 2001:3::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:5::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:2:1::/64 2001::2
+ipv6 route 2001:db8:2:2::/64 2001::2
+ipv6 route 2001:db8:2:3::/64 2001::2
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf
new file mode 100644
index 0000000..9a7831d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/bgpd.conf
@@ -0,0 +1,67 @@
+frr defaults traditional
+!
+bgp send-extra-data zebra
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp updates
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 2
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::1 remote-as 1
+ neighbor 2001::1 timers 3 10
+ neighbor 2001::1 timers connect 1
+ !
+ address-family ipv6 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 2 vrf vrf10
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 2:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 2 vrf vrf20
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ !
+ address-family ipv6 unicast
+ sid vpn export auto
+ rd vpn export 2:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json
new file mode 100644
index 0000000..2cd47b9
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib.json
@@ -0,0 +1,170 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json
new file mode 100644
index 0000000..25cdf03
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_deleted.json
@@ -0,0 +1,93 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json
new file mode 100644
index 0000000..03bbcc0
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vpnv6_rib_locator_recreated.json
@@ -0,0 +1,169 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "2001:1::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:1::",
+ "prefixLen": 64,
+ "network": "2001:1::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:3::",
+ "prefixLen": 64,
+ "network": "2001:3::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "2001:5::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:5::",
+ "prefixLen": 64,
+ "network": "2001:5::/64",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "2001:2::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:2::",
+ "prefixLen": 64,
+ "network": "2001:2::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "2001:4::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:4::",
+ "prefixLen": 64,
+ "network": "2001:4::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "2001:6::",
+ "prefixLen": 64,
+ "network": "2001:6::/64",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "::",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json
new file mode 100644
index 0000000..7f8a930
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf10_rib.json
@@ -0,0 +1,92 @@
+{
+ "2001:1::/64": [
+ {
+ "prefix": "2001:1::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:100::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ],
+ "2001:2::/64": [
+ {
+ "prefix": "2001:2::/64",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:3::/64": [
+ {
+ "prefix": "2001:3::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:100::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json
new file mode 100644
index 0000000..104bdc3
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/vrf20_rib.json
@@ -0,0 +1,86 @@
+{
+ "2001:4::/64": [
+ {
+ "prefix": "2001:4::/64",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth2",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "2001:5::/64": [
+ {
+ "prefix": "2001:5::/64",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:200::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ],
+ "2001:6::/64": [
+ {
+ "prefix": "2001:6::/64",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth3",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf
new file mode 100644
index 0000000..51d9c92
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/r2/zebra.conf
@@ -0,0 +1,42 @@
+log file zebra.log
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+! debug zebra packet
+! debug zebra dplane
+! debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::2/64
+!
+interface eth1 vrf vrf10
+ ipv6 address 2001:2::1/64
+!
+interface eth2 vrf vrf20
+ ipv6 address 2001:4::1/64
+!
+interface eth3 vrf vrf20
+ ipv6 address 2001:6::1/64
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:2:2::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:1:1::/64 2001::1
+ipv6 route 2001:db8:1:2::/64 2001::1
+ipv6 route 2001:db8:1:3::/64 2001::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
new file mode 100755
index 0000000..10d890e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf/test_bgp_srv6l3vpn_to_bgp_vrf.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import sys
+import json
+import functools
+import pytest
+
+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 required_linux_kernel_version
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ r"""
+ CE1 CE3 CE5
+ (eth0) (eth0) (eth0)
+ :2 :2 :2
+ | | |
+ 2001: 2001: 2001:
+ 1::/64 3::/64 5::/64
+ | | |
+ :1 :1 :1
+ +-(eth1)--(eth2)---(eth3)-+
+ | \ / | |
+ | (vrf10) (vrf20) |
+ | R1 |
+ +----------(eth0)---------+
+ :1
+ |
+ 2001::/64
+ |
+ :2
+ (eth0)
+ +----------(eth0)--------------+
+ | R2 |
+ | (vrf10) (vrf20) |
+ | / / \ |
+ +-(eth1)-----(eth2)-----(eth3)-+
+ :1 :1 :1
+ | | |
+ +------+ +------+ +------+
+ / 2001: \ / 2001: \ / 2001: \
+ \ 2::/64 / \ 4::/64 / \ 6::/64 /
+ +------+ +------+ +------+
+ | | |
+ :2 :2 :2
+ (eth0) (eth0) (eth0)
+ CE2 CE4 CE6
+ """
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("ce1")
+ tgen.add_router("ce2")
+ tgen.add_router("ce3")
+ tgen.add_router("ce4")
+ tgen.add_router("ce5")
+ tgen.add_router("ce6")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3")
+ tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3")
+
+
+def setup_module(mod):
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in tgen.routers().items():
+ router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname))
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r1"].run("ip link set vrf10 up")
+ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r1"].run("ip link set vrf20 up")
+ tgen.gears["r1"].run("ip link set eth1 master vrf10")
+ tgen.gears["r1"].run("ip link set eth2 master vrf10")
+ tgen.gears["r1"].run("ip link set eth3 master vrf20")
+
+ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r2"].run("ip link set vrf10 up")
+ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r2"].run("ip link set vrf20 up")
+ tgen.gears["r2"].run("ip link set eth1 master vrf10")
+ tgen.gears["r2"].run("ip link set eth2 master vrf20")
+ tgen.gears["r2"].run("ip link set eth3 master vrf20")
+ tgen.start_router()
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def check_ping(name, dest_addr, expect_connected):
+ def _check(name, dest_addr, match):
+ tgen = get_topogen()
+ output = tgen.gears[name].run("ping6 {} -c 1 -w 1".format(dest_addr))
+ logger.info(output)
+ if match not in output:
+ return "ping fail"
+
+ match = "{} packet loss".format("0%" if expect_connected else "100%")
+ logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, dest_addr, match)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=1)
+ assert result is None, "Failed"
+
+
+def check_rib(name, cmd, expected_file):
+ def _check(name, cmd, expected_file):
+ logger.info("polling")
+ tgen = get_topogen()
+ router = tgen.gears[name]
+ output = json.loads(router.vtysh_cmd(cmd))
+ expected = open_json_file("{}/{}".format(CWD, expected_file))
+ return topotest.json_cmp(output, expected)
+
+ logger.info('[+] check {} "{}" {}'.format(name, cmd, expected_file))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf10 json", "r1/vrf10_rib.json")
+ check_rib("r1", "show ipv6 route vrf vrf20 json", "r1/vrf20_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf10 json", "r2/vrf10_rib.json")
+ check_rib("r2", "show ipv6 route vrf vrf20 json", "r2/vrf20_rib.json")
+ check_rib("ce1", "show ipv6 route json", "ce1/ipv6_rib.json")
+ check_rib("ce2", "show ipv6 route json", "ce2/ipv6_rib.json")
+ check_rib("ce3", "show ipv6 route json", "ce3/ipv6_rib.json")
+ check_rib("ce4", "show ipv6 route json", "ce4/ipv6_rib.json")
+ check_rib("ce5", "show ipv6 route json", "ce5/ipv6_rib.json")
+ check_rib("ce6", "show ipv6 route json", "ce6/ipv6_rib.json")
+
+
+def test_ping():
+ check_ping("ce1", "2001:2::2", True)
+ check_ping("ce1", "2001:3::2", True)
+ check_ping("ce1", "2001:4::2", False)
+ check_ping("ce1", "2001:5::2", False)
+ check_ping("ce1", "2001:6::2", False)
+ check_ping("ce4", "2001:1::2", False)
+ check_ping("ce4", "2001:2::2", False)
+ check_ping("ce4", "2001:3::2", False)
+ check_ping("ce4", "2001:5::2", True)
+ check_ping("ce4", "2001:6::2", True)
+
+
+def test_locator_delete():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_locator_recreate():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
+
+
+def test_bgp_locator_unset():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ no locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_bgp_locator_reset():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
+
+
+def test_bgp_srv6_unset():
+ check_ping("ce1", "2001:2::2", True)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ no segment-routing srv6
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_deleted.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_deleted.json")
+ check_ping("ce1", "2001:2::2", False)
+
+
+def test_bgp_srv6_reset():
+ check_ping("ce1", "2001:2::2", False)
+ get_topogen().gears["r1"].vtysh_cmd(
+ """
+ configure terminal
+ router bgp 1
+ segment-routing srv6
+ locator loc1
+ """
+ )
+ check_rib("r1", "show bgp ipv6 vpn json", "r1/vpnv6_rib_locator_recreated.json")
+ check_rib("r2", "show bgp ipv6 vpn json", "r2/vpnv6_rib_locator_recreated.json")
+ check_ping("ce1", "2001:2::2", True)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf
new file mode 100644
index 0000000..3459796
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json
new file mode 100644
index 0000000..1d33fee
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.1.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.1.0/24": [
+ {
+ "prefix": "192.168.1.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf
new file mode 100644
index 0000000..447d1b4
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce1/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface eth0
+ ip address 192.168.1.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.1.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf
new file mode 100644
index 0000000..8ed9978
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce2
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json
new file mode 100644
index 0000000..a21f4a1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.2.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.2.0/24": [
+ {
+ "prefix": "192.168.2.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf
new file mode 100644
index 0000000..1165225
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce2/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface eth0
+ ip address 192.168.2.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.2.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf
new file mode 100644
index 0000000..a85d970
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce3
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json
new file mode 100644
index 0000000..38a7807
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.3.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24": [
+ {
+ "prefix": "192.168.3.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf
new file mode 100644
index 0000000..299c659
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce3/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface eth0
+ ip address 192.168.3.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.3.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf
new file mode 100644
index 0000000..93fb32f
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce4
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json
new file mode 100644
index 0000000..a0be78e
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.4.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.4.0/24": [
+ {
+ "prefix": "192.168.4.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf
new file mode 100644
index 0000000..30f3736
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce4/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce4
+!
+interface eth0
+ ip address 192.168.4.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.4.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf
new file mode 100644
index 0000000..2ab6f2d
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce5
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json
new file mode 100644
index 0000000..dc338d5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.5.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.5.0/24": [
+ {
+ "prefix": "192.168.5.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf
new file mode 100644
index 0000000..208dcb1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce5/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce5
+!
+interface eth0
+ ip address 192.168.5.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.5.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf
new file mode 100644
index 0000000..e0b6540
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/bgpd.conf
@@ -0,0 +1,8 @@
+frr defaults traditional
+!
+hostname ce6
+password zebra
+!
+log stdout notifications
+log commands
+log file bgpd.log
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json
new file mode 100644
index 0000000..4a603a5
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/ip_rib.json
@@ -0,0 +1,58 @@
+{
+ "0.0.0.0/0": [
+ {
+ "prefix": "0.0.0.0/0",
+ "protocol": "static",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 1,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 73,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.6.1",
+ "afi": "ipv4",
+ "interfaceName": "eth0",
+ "active": true,
+ "weight": 1
+ }
+ ]
+ }
+ ],
+ "192.168.6.0/24": [
+ {
+ "prefix": "192.168.6.0/24",
+ "protocol": "connected",
+ "vrfId": 0,
+ "vrfName": "default",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 254,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf
new file mode 100644
index 0000000..d68a008
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/ce6/zebra.conf
@@ -0,0 +1,14 @@
+log file zebra.log
+!
+hostname ce6
+!
+interface eth0
+ ip address 192.168.6.2/24
+!
+ip forwarding
+ipv6 forwarding
+!
+ip route 0.0.0.0/0 192.168.6.1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf
new file mode 100644
index 0000000..c061751
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/bgpd.conf
@@ -0,0 +1,66 @@
+frr defaults traditional
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 1
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::2 remote-as 2
+ neighbor 2001::2 timers 3 10
+ neighbor 2001::2 timers connect 1
+ neighbor 2001::2 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::2 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 1 vrf vrf10
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv4 unicast
+ sid vpn export auto
+ nexthop vpn export 2001::1
+ rd vpn export 1:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ !
+ exit-address-family
+!
+router bgp 1 vrf vrf20
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv4 unicast
+ sid vpn export auto
+ nexthop vpn export 2001::1
+ rd vpn export 1:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json
new file mode 100644
index 0000000..3cc2fdd
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vpnv4_rib.json
@@ -0,0 +1,167 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "1.1.1.1",
+ "defaultLocPrf": 100,
+ "localAS": 1,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "192.168.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.1.0",
+ "prefixLen": 24,
+ "network": "192.168.1.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.3.0",
+ "prefixLen": 24,
+ "network": "192.168.3.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "192.168.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.5.0",
+ "prefixLen": 24,
+ "network": "192.168.5.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "192.168.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.2.0",
+ "prefixLen": 24,
+ "network": "192.168.2.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "192.168.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.4.0",
+ "prefixLen": 24,
+ "network": "192.168.4.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.168.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.6.0",
+ "prefixLen": 24,
+ "network": "192.168.6.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::2",
+ "path": "2",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json
new file mode 100644
index 0000000..8daa9b1
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf10_rib.json
@@ -0,0 +1,86 @@
+{
+ "192.168.1.0/24": [
+ {
+ "prefix": "192.168.1.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0/24": [
+ {
+ "prefix": "192.168.2.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:100::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ],
+ "192.168.3.0/24": [
+ {
+ "prefix": "192.168.3.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth2",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json
new file mode 100644
index 0000000..6f123cf
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/vrf20_rib.json
@@ -0,0 +1,92 @@
+{
+ "192.168.4.0/24": [
+ {
+ "prefix": "192.168.4.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:200::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ],
+ "192.168.5.0/24": [
+ {
+ "prefix": "192.168.5.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth3",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "192.168.6.0/24": [
+ {
+ "prefix": "192.168.6.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:2:2:200::"
+ }
+ }
+ ],
+ "asPath": "2"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf
new file mode 100644
index 0000000..a43cec2
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r1/zebra.conf
@@ -0,0 +1,41 @@
+log file zebra.log
+!
+hostname r1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+debug zebra packet
+debug zebra dplane
+debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::1/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.1.1/24
+ ipv6 address 2001:1::1/64
+!
+interface eth2 vrf vrf10
+ ip address 192.168.3.1/24
+!
+interface eth3 vrf vrf20
+ ip address 192.168.5.1/24
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:2:2::/64 2001::2
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf
new file mode 100644
index 0000000..0517057
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/bgpd.conf
@@ -0,0 +1,66 @@
+frr defaults traditional
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+!debug bgp neighbor-events
+!debug bgp zebra
+!debug bgp vnc verbose
+!debug bgp update-groups
+!debug bgp updates in
+!debug bgp updates out
+!debug bgp updates
+!debug bgp vpn label
+!debug bgp vpn leak-from-vrf
+!debug bgp vpn leak-to-vrf
+!debug bgp vpn rmap-event
+!
+router bgp 2
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor 2001::1 remote-as 1
+ neighbor 2001::1 timers 3 10
+ neighbor 2001::1 timers connect 1
+ neighbor 2001::1 capability extended-nexthop
+ !
+ address-family ipv4 vpn
+ neighbor 2001::1 activate
+ exit-address-family
+ !
+ segment-routing srv6
+ locator loc1
+ !
+!
+router bgp 2 vrf vrf10
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv4 unicast
+ sid vpn export auto
+ nexthop vpn export 2001::2
+ rd vpn export 2:10
+ rt vpn both 99:99
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
+router bgp 2 vrf vrf20
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ !
+ address-family ipv4 unicast
+ sid vpn export auto
+ nexthop vpn export 2001::2
+ rd vpn export 2:20
+ rt vpn both 88:88
+ import vpn
+ export vpn
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json
new file mode 100644
index 0000000..9557054
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vpnv4_rib.json
@@ -0,0 +1,167 @@
+{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 2,
+ "routerId": "2.2.2.2",
+ "defaultLocPrf": 100,
+ "localAS": 2,
+ "routes": {
+ "routeDistinguishers": {
+ "1:10": {
+ "192.168.1.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.1.0",
+ "prefixLen": 24,
+ "network": "192.168.1.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.3.0",
+ "prefixLen": 24,
+ "network": "192.168.3.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "1:20": {
+ "192.168.5.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.5.0",
+ "prefixLen": 24,
+ "network": "192.168.5.0/24",
+ "metric": 0,
+ "weight": 0,
+ "peerId": "2001::1",
+ "path": "1",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "ip": "2001::1",
+ "hostname": "r1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:10": {
+ "192.168.2.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.2.0",
+ "prefixLen": 24,
+ "network": "192.168.2.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ },
+ "2:20": {
+ "192.168.4.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.4.0",
+ "prefixLen": 24,
+ "network": "192.168.4.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "192.168.6.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "selectionReason": "First path received",
+ "pathFrom": "external",
+ "prefix": "192.168.6.0",
+ "prefixLen": 24,
+ "network": "192.168.6.0/24",
+ "metric": 0,
+ "weight": 32768,
+ "peerId": "(unspec)",
+ "path": "",
+ "origin": "incomplete",
+ "nhVrfName": "vrf20",
+ "nexthops": [
+ {
+ "ip": "2001::2",
+ "hostname": "r2",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json
new file mode 100644
index 0000000..6268031
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf10_rib.json
@@ -0,0 +1,92 @@
+{
+ "192.168.1.0/24": [
+ {
+ "prefix": "192.168.1.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:100::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ],
+ "192.168.2.0/24": [
+ {
+ "prefix": "192.168.2.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24": [
+ {
+ "prefix": "192.168.3.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf10",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 10,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:100::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json
new file mode 100644
index 0000000..ffe2e07
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/vrf20_rib.json
@@ -0,0 +1,86 @@
+{
+ "192.168.4.0/24": [
+ {
+ "prefix": "192.168.4.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth2",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "192.168.5.0/24": [
+ {
+ "prefix": "192.168.5.0/24",
+ "protocol": "bgp",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "eth0",
+ "vrf": "default",
+ "active": true,
+ "weight": 1,
+ "seg6": {
+ "segs": "2001:db8:1:1:200::"
+ }
+ }
+ ],
+ "asPath": "1"
+ }
+ ],
+ "192.168.6.0/24": [
+ {
+ "prefix": "192.168.6.0/24",
+ "protocol": "connected",
+ "vrfName": "vrf20",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "table": 20,
+ "internalStatus": 16,
+ "internalFlags": 8,
+ "internalNextHopNum": 1,
+ "internalNextHopActiveNum": 1,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "eth3",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf
new file mode 100644
index 0000000..71ddedf
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/r2/zebra.conf
@@ -0,0 +1,40 @@
+log file zebra.log
+!
+hostname r2
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+debug zebra packet
+debug zebra dplane
+debug zebra kernel
+!
+interface eth0
+ ipv6 address 2001::2/64
+!
+interface eth1 vrf vrf10
+ ip address 192.168.2.1/24
+!
+interface eth2 vrf vrf20
+ ip address 192.168.4.1/24
+!
+interface eth3 vrf vrf20
+ ip address 192.168.6.1/24
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:2:2::/64
+ !
+ !
+!
+ip forwarding
+ipv6 forwarding
+!
+ipv6 route 2001:db8:1:1::/64 2001::1
+!
+line vty
+!
diff --git a/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py
new file mode 100755
index 0000000..af66a5a
--- /dev/null
+++ b/tests/topotests/bgp_srv6l3vpn_to_bgp_vrf2/test_bgp_srv6l3vpn_to_bgp_vrf2.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018, LabN Consulting, L.L.C.
+# Authored by Lou Berger <lberger@labn.net>
+#
+# 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.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+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 required_linux_kernel_version
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+ tgen.add_router("ce1")
+ tgen.add_router("ce2")
+ tgen.add_router("ce3")
+ tgen.add_router("ce4")
+ tgen.add_router("ce5")
+ tgen.add_router("ce6")
+
+ tgen.add_link(tgen.gears["r1"], tgen.gears["r2"], "eth0", "eth0")
+ tgen.add_link(tgen.gears["ce1"], tgen.gears["r1"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce2"], tgen.gears["r2"], "eth0", "eth1")
+ tgen.add_link(tgen.gears["ce3"], tgen.gears["r1"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce4"], tgen.gears["r2"], "eth0", "eth2")
+ tgen.add_link(tgen.gears["ce5"], tgen.gears["r1"], "eth0", "eth3")
+ tgen.add_link(tgen.gears["ce6"], tgen.gears["r2"], "eth0", "eth3")
+
+
+def setup_module(mod):
+ result = required_linux_kernel_version("5.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ for rname, router in tgen.routers().items():
+ router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname))
+ router.load_config(TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)))
+ router.load_config(TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname)))
+
+ tgen.gears["r1"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r1"].run("ip link set vrf10 up")
+ tgen.gears["r1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r1"].run("ip link set vrf20 up")
+ tgen.gears["r1"].run("ip link set eth1 master vrf10")
+ tgen.gears["r1"].run("ip link set eth2 master vrf10")
+ tgen.gears["r1"].run("ip link set eth3 master vrf20")
+
+ tgen.gears["r2"].run("sysctl net.vrf.strict_mode=1")
+ tgen.gears["r2"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["r2"].run("ip link set vrf10 up")
+ tgen.gears["r2"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["r2"].run("ip link set vrf20 up")
+ tgen.gears["r2"].run("ip link set eth1 master vrf10")
+ tgen.gears["r2"].run("ip link set eth2 master vrf20")
+ tgen.gears["r2"].run("ip link set eth3 master vrf20")
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def check_ping(name, dest_addr, expect_connected):
+ def _check(name, dest_addr, match):
+ tgen = get_topogen()
+ output = tgen.gears[name].run("ping {} -c 1 -w 1".format(dest_addr))
+ logger.info(output)
+ assert match in output, "ping fail"
+
+ match = "{} packet loss".format("0%" if expect_connected else "100%")
+ logger.info("[+] check {} {} {}".format(name, dest_addr, match))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, dest_addr, match)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def check_rib(name, cmd, expected_file):
+ def _check(name, dest_addr, match):
+ logger.info("polling")
+ tgen = get_topogen()
+ router = tgen.gears[name]
+ output = json.loads(router.vtysh_cmd(cmd))
+ expected = open_json_file("{}/{}".format(CWD, expected_file))
+ return topotest.json_cmp(output, expected)
+
+ logger.info("[+] check {} \"{}\" {}".format(name, cmd, expected_file))
+ tgen = get_topogen()
+ func = functools.partial(_check, name, cmd, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check_rib("r1", "show bgp ipv4 vpn json", "r1/vpnv4_rib.json")
+ check_rib("r2", "show bgp ipv4 vpn json", "r2/vpnv4_rib.json")
+ check_rib("r1", "show ip route vrf vrf10 json", "r1/vrf10_rib.json")
+ check_rib("r1", "show ip route vrf vrf20 json", "r1/vrf20_rib.json")
+ check_rib("r2", "show ip route vrf vrf10 json", "r2/vrf10_rib.json")
+ check_rib("r2", "show ip route vrf vrf20 json", "r2/vrf20_rib.json")
+ check_rib("ce1", "show ip route json", "ce1/ip_rib.json")
+ check_rib("ce2", "show ip route json", "ce2/ip_rib.json")
+ check_rib("ce3", "show ip route json", "ce3/ip_rib.json")
+ check_rib("ce4", "show ip route json", "ce4/ip_rib.json")
+ check_rib("ce5", "show ip route json", "ce5/ip_rib.json")
+ check_rib("ce6", "show ip route json", "ce6/ip_rib.json")
+
+
+def test_ping():
+ check_ping("ce1", "192.168.2.2", " 0% packet loss")
+ check_ping("ce1", "192.168.3.2", " 0% packet loss")
+ check_ping("ce1", "192.168.4.2", " 100% packet loss")
+ check_ping("ce1", "192.168.5.2", " 100% packet loss")
+ check_ping("ce1", "192.168.6.2", " 100% packet loss")
+ check_ping("ce4", "192.168.1.2", " 100% packet loss")
+ check_ping("ce4", "192.168.2.2", " 100% packet loss")
+ check_ping("ce4", "192.168.3.2", " 100% packet loss")
+ check_ping("ce4", "192.168.5.2", " 0% packet loss")
+ check_ping("ce4", "192.168.6.2", " 0% packet loss")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json
new file mode 100644
index 0000000..1a5ede2
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r1/bgp_ipv4_allowas.json
@@ -0,0 +1,40 @@
+{
+ "prefix":"192.168.1.1/32",
+ "paths":[
+ {
+ "aspath":{
+ "string":"2",
+ "segments":[
+ {
+ "type":"as-sequence",
+ "list":[
+ 2
+ ]
+ }
+ ],
+ "length":1
+ },
+ "origin":"incomplete",
+ "metric":0,
+ "valid":true,
+ "bestpath":{
+ "overall":true,
+ "selectionReason":"First path received"
+ },
+ "nexthops":[
+ {
+ "ip":"10.0.0.2",
+ "afi":"ipv4",
+ "metric":0,
+ "accessible":true,
+ "used":true
+ }
+ ],
+ "peer":{
+ "peerId":"10.0.0.2",
+ "routerId":"60.0.0.1",
+ "type":"external"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r1/bgpd.conf b/tests/topotests/bgp_suppress_fib/r1/bgpd.conf
new file mode 100644
index 0000000..69c563d
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r1/bgpd.conf
@@ -0,0 +1,15 @@
+! exit1
+router bgp 1
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.2 remote-as 2
+
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 10.0.0.2 route-map rmap out
+ exit-address-family
+
+ip prefix-list plist seq 5 permit any
+
+route-map rmap permit 1
+ match ip address prefix-list plist
+!
diff --git a/tests/topotests/bgp_suppress_fib/r1/zebra.conf b/tests/topotests/bgp_suppress_fib/r1/zebra.conf
new file mode 100644
index 0000000..7b44216
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r1/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface r1-eth0
+ ip address 10.0.0.1/30
+!
+ip forwarding
+!
+ip route 40.0.0.0/8 blackhole
+ip route 50.0.0.0/8 blackhole
+!
diff --git a/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json
new file mode 100644
index 0000000..4a35abf
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/bgp_ipv4_allowas.json
@@ -0,0 +1,68 @@
+{
+ "prefix":"192.168.1.1/32",
+ "paths":[
+ {
+ "aspath":{
+ "string":"1 2",
+ "segments":[
+ {
+ "type":"as-sequence",
+ "list":[
+ 1,
+ 2
+ ]
+ }
+ ],
+ "length":2
+ },
+ "origin":"incomplete",
+ "valid":true,
+ "fibInstalled":true,
+ "nexthops":[
+ {
+ "ip":"10.0.0.1",
+ "afi":"ipv4",
+ "metric":0,
+ "accessible":true,
+ "used":true
+ }
+ ],
+ "peer":{
+ "peerId":"10.0.0.1",
+ "routerId":"10.0.0.1",
+ "type":"external"
+ }
+ },
+ {
+ "aspath":{
+ "string":"Local",
+ "segments":[
+ ],
+ "length":0
+ },
+ "origin":"incomplete",
+ "metric":0,
+ "weight":32768,
+ "valid":true,
+ "sourced":true,
+ "bestpath":{
+ "overall":true,
+ "selectionReason":"Weight"
+ },
+ "fibInstalled":true,
+ "nexthops":[
+ {
+ "ip":"10.0.0.10",
+ "afi":"ipv4",
+ "metric":0,
+ "accessible":true,
+ "used":true
+ }
+ ],
+ "peer":{
+ "peerId":"0.0.0.0",
+ "routerId":"60.0.0.1"
+ }
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf
new file mode 100644
index 0000000..caebb0e
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.allowas_in.conf
@@ -0,0 +1,18 @@
+access-list access seq 10 permit 192.168.1.1/32
+!
+ip route 192.168.1.1/32 10.0.0.10
+!
+debug bgp bestpath
+debug bgp nht
+debug bgp updates
+debug bgp update-groups
+debug bgp zebra
+debug zebra rib detail
+!
+router bgp 2
+ address-family ipv4 uni
+ redistribute static
+ neighbor 10.0.0.10 allowas-in 1
+ neighbor 10.0.0.1 allowas-in 1
+ !
+!
diff --git a/tests/topotests/bgp_suppress_fib/r2/bgpd.conf b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf
new file mode 100644
index 0000000..010e86a
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/bgpd.conf
@@ -0,0 +1,11 @@
+debug bgp updates
+debug bgp bestpath 40.0.0.0/8
+debug bgp zebra
+!
+router bgp 2
+ no bgp ebgp-requires-policy
+ bgp suppress-fib-pending
+ neighbor 10.0.0.1 remote-as 1
+ neighbor 10.0.0.10 remote-as 3
+ address-family ipv4 uni
+ network 60.0.0.0/24 \ No newline at end of file
diff --git a/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json b/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/no_bgp_ipv4_allowas.json
@@ -0,0 +1 @@
+{}
diff --git a/tests/topotests/bgp_suppress_fib/r2/v4_override.json b/tests/topotests/bgp_suppress_fib/r2/v4_override.json
new file mode 100644
index 0000000..f17907f
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/v4_override.json
@@ -0,0 +1,20 @@
+{
+ "40.0.0.0\/8":[
+ {
+ "prefix":"40.0.0.0\/8",
+ "protocol":"static",
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.0.10",
+ "afi":"ipv4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r2/zebra.conf b/tests/topotests/bgp_suppress_fib/r2/zebra.conf
new file mode 100644
index 0000000..6e8bce0
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r2/zebra.conf
@@ -0,0 +1,16 @@
+!
+interface lo
+ ip address 60.0.0.1/24
+!
+interface r2-eth0
+ ip address 10.0.0.2/30
+!
+interface r2-eth1
+ ip address 10.0.0.9/30
+
+access-list access seq 5 permit 40.0.0.0/8
+
+route-map LIMIT permit 10
+ match ip address access
+
+ip protocol bgp route-map LIMIT
diff --git a/tests/topotests/bgp_suppress_fib/r3/bgpd.conf b/tests/topotests/bgp_suppress_fib/r3/bgpd.conf
new file mode 100644
index 0000000..11715d4
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/bgpd.conf
@@ -0,0 +1,9 @@
+!
+router bgp 3
+ no bgp ebgp-requires-policy
+ neighbor 10.0.0.9 remote-as 2
+
+route-map rmap permit 1
+ match ip address prefix-list plist
+ !
+!
diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_override.json b/tests/topotests/bgp_suppress_fib/r3/v4_override.json
new file mode 100644
index 0000000..a35d49e
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/v4_override.json
@@ -0,0 +1,4 @@
+{
+ "0.0.0.0\/0":[
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route.json b/tests/topotests/bgp_suppress_fib/r3/v4_route.json
new file mode 100644
index 0000000..e255d07
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/v4_route.json
@@ -0,0 +1,28 @@
+{
+ "40.0.0.0\/8":[
+ {
+ "prefix":"40.0.0.0\/8",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "internalStatus":16,
+ "internalFlags":8,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"10.0.0.9",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route2.json b/tests/topotests/bgp_suppress_fib/r3/v4_route2.json
new file mode 100644
index 0000000..a35d49e
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/v4_route2.json
@@ -0,0 +1,4 @@
+{
+ "0.0.0.0\/0":[
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r3/v4_route3.json b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json
new file mode 100644
index 0000000..ab8c3aa
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/v4_route3.json
@@ -0,0 +1,23 @@
+{
+ "60.0.0.0/24":[
+ {
+ "prefix":"60.0.0.0/24",
+ "protocol":"bgp",
+ "selected":true,
+ "destSelected":true,
+ "distance":20,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.0.9",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_suppress_fib/r3/zebra.conf b/tests/topotests/bgp_suppress_fib/r3/zebra.conf
new file mode 100644
index 0000000..793b043
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/r3/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r3-eth0
+ ip address 10.0.0.10/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
new file mode 100644
index 0000000..96a294c
--- /dev/null
+++ b/tests/topotests/bgp_suppress_fib/test_bgp_suppress_fib.py
@@ -0,0 +1,235 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_suppress_fib.py
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+"""
+
+import os
+import sys
+import json
+import pytest
+from functools import partial
+from time import sleep
+from lib.topolog import logger
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ 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"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_route():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r3 = tgen.gears["r3"]
+
+ json_file = "{}/r3/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r3,
+ "show ip route 40.0.0.0 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"r3" JSON output mismatches'
+ assert result is None, assertmsg
+
+ json_file = "{}/r3/v4_route2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r3,
+ "show ip route 50.0.0.0 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ assertmsg = '"r3" JSON output mismatches'
+ assert result is None, assertmsg
+
+ json_file = "{}/r3/v4_route3.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r3,
+ "show ip route 10.0.0.3 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+
+def test_bgp_better_admin_won():
+ "A better Admin distance protocol may come along and knock us out"
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r2.vtysh_cmd("conf\nip route 40.0.0.0/8 10.0.0.10")
+
+ json_file = "{}/r2/v4_override.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ logger.info(expected)
+ test_func = partial(
+ topotest.router_json_cmp, r2, "show ip route 40.0.0.0 json", expected
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r2" static route did not take over'
+ assert result is None, assertmsg
+
+ r3 = tgen.gears["r3"]
+
+ json_file = "{}/r3/v4_override.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r3, "show ip route 40.0.0.0 json", expected
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r3" route to 40.0.0.0 should have been lost'
+ assert result is None, assertmsg
+
+ r2.vtysh_cmd("conf\nno ip route 40.0.0.0/8 10.0.0.10")
+
+ json_file = "{}/r3/v4_route.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r3,
+ "show ip route 40.0.0.0 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r3" route to 40.0.0.0 did not come back'
+ assert result is None, assertmsg
+
+
+def test_bgp_allow_as_in():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+
+ config_file = "{}/r2/bgpd.allowas_in.conf".format(CWD)
+ r2.run("vtysh -f {}".format(config_file))
+
+ json_file = "{}/r2/bgp_ipv4_allowas.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r2,
+ "show bgp ipv4 uni 192.168.1.1/32 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r2" static redistribution failed into bgp'
+ assert result is None, assertmsg
+
+ r1 = tgen.gears["r1"]
+
+ json_file = "{}/r1/bgp_ipv4_allowas.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show bgp ipv4 uni 192.168.1.1/32 json",
+ expected,
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r1" 192.168.1.1/32 route should have arrived'
+ assert result is None, assertmsg
+
+ r2.vtysh_cmd("conf\nno ip route 192.168.1.1/32 10.0.0.10")
+
+ json_file = "{}/r2/no_bgp_ipv4_allowas.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r2,
+ "show bgp ipv4 uni 192.168.1.1/32 json",
+ expected,
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"r2" 192.168.1.1/32 route should be gone'
+ assert result is None, assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_tcp_mss/__init__.py b/tests/topotests/bgp_tcp_mss/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/__init__.py
diff --git a/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json b/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json
new file mode 100644
index 0000000..17cee03
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/bgp_vrf_tcp_mss.json
@@ -0,0 +1,222 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "20.20.20.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.10.10.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 24,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "vrf": "RED"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "vrfs": [
+ {
+ "name": "RED",
+ "id": "1"
+ }
+ ],
+ "bgp": [
+ {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/bgp_tcp_mss/r1/bgpd.conf b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf
new file mode 100644
index 0000000..07cfe2e
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/r1/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65000
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.255.2 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_tcp_mss/r1/zebra.conf b/tests/topotests/bgp_tcp_mss/r1/zebra.conf
new file mode 100644
index 0000000..6e9b0b4
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 192.168.255.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_tcp_mss/r2/bgpd.conf b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf
new file mode 100644
index 0000000..b2d9455
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/r2/bgpd.conf
@@ -0,0 +1,6 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65000
+ neighbor 192.168.255.1 timers 3 10
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_tcp_mss/r2/zebra.conf b/tests/topotests/bgp_tcp_mss/r2/zebra.conf
new file mode 100644
index 0000000..6c14de5
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/r2/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r2-eth0
+ ip address 192.168.255.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
new file mode 100644
index 0000000..eed0b34
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_tcp_mss.py
@@ -0,0 +1,175 @@
+#!/usr/bin/env python
+
+#
+# bgp_tcp_mss.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Abhinay Ramesh <rabhinay@vmware.com>
+#
+# 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.
+#
+
+"""
+bgp_tcp_mss.py:
+
+Test if works the following commands:
+router bgp 65000
+ neighbor 192.168.255.2 tcp-mss 500
+
+Need to verify if the tcp-mss value is reflected in the TCP session.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+# add after imports, before defining classes or functions:
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_tcp_mss():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 0}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_conf_tcp_mss(router, as_num, neigh):
+ router.vtysh_cmd(
+ """configure terminal
+ router bgp {0}
+ neighbor {1} tcp-mss 500""".format(
+ as_num, neigh
+ )
+ )
+
+ def _bgp_clear_session(router):
+ router.vtysh_cmd("clear bgp *")
+
+ def _bgp_check_neighbor_tcp_mss(router, neigh):
+ output = json.loads(router.vtysh_cmd("show bgp neighbor {} json".format(neigh)))
+ expected = {
+ "{}".format(neigh): {"bgpTcpMssConfigured": 500, "bgpTcpMssSynced": 488}
+ }
+ return topotest.json_cmp(output, expected)
+
+ logger.info("Check if neighbor sessions are up in {}".format(router1.name))
+ test_func = functools.partial(_bgp_converge, router1)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+ assert result is None, 'Failed to see BGP convergence in "{}"'.format(router1.name)
+
+ logger.info("BGP neighbor session is up in {}".format(router1.name))
+
+ logger.info(
+ "Configure tcp-mss 500 on {} and reset the session".format(router1.name)
+ )
+ _bgp_conf_tcp_mss(router1, "65000", "192.168.255.2")
+ _bgp_clear_session(router1)
+
+ logger.info(
+ "Configure tcp-mss 500 on {} and reset the session".format(router2.name)
+ )
+ _bgp_conf_tcp_mss(router2, "65001", "192.168.255.1")
+ _bgp_clear_session(router2)
+
+ logger.info(
+ "Check if neighbor session is up after reset in {}".format(router1.name)
+ )
+ test_func = functools.partial(_bgp_converge, router1)
+ success, result = topotest.run_and_expect(test_func, None, count=15, wait=0.5)
+ assert result is None, 'Failed to see BGP convergence after reset in "{}"'.format(
+ router1.name
+ )
+
+ logger.info(
+ "Verify if TCP MSS value is synced with neighbor in {}".format(router1.name)
+ )
+ test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router1, "192.168.255.2")
+ success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ assert (
+ result is None
+ ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router1.name)
+ logger.info("TCP MSS value is synced with neighbor in {}".format(router1.name))
+
+ logger.info(
+ "Verify if TCP MSS value is synced with neighbor in {}".format(router2.name)
+ )
+ test_func = functools.partial(_bgp_check_neighbor_tcp_mss, router2, "192.168.255.1")
+ success, result = topotest.run_and_expect(test_func, None, count=3, wait=0.5)
+ assert (
+ result is None
+ ), 'Failed to sync TCP MSS value over BGP session in "{}"'.format(router2.name)
+ logger.info("TCP MSS value is synced with neighbor in {}".format(router2.name))
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py b/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py
new file mode 100644
index 0000000..7094c28
--- /dev/null
+++ b/tests/topotests/bgp_tcp_mss/test_bgp_vrf_tcp_mss.py
@@ -0,0 +1,766 @@
+#!/usr/bin/env python
+
+#
+# bgp_tcp_mss.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by
+# Shreenidhi A R <rshreenidhi@vmware.com>
+#
+# 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.
+#
+
+"""
+bgp_tcp_mss_vrf.py:
+
+Need to verify if the tcp-mss value is reflected in the TCP session and in VRF.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+import platform
+import socket
+import subprocess
+
+# add after imports, before defining classes or functions:
+pytestmark = [pytest.mark.bgpd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+import time
+from lib.bgp import (
+ clear_bgp,
+ clear_bgp_and_verify,
+ create_router_bgp,
+ modify_as_number,
+ verify_as_numbers,
+ verify_bgp_convergence,
+ verify_bgp_rib,
+ verify_bgp_timers_and_functionality,
+ verify_router_id,
+ verify_tcp_mss
+)
+from lib.common_config import (
+ kill_router_daemons,
+ start_router_daemons,
+ addKernelRoute,
+ apply_raw_config,
+ check_address_types,
+ create_prefix_lists,
+ create_route_maps,
+ create_static_routes,
+ required_linux_kernel_version,
+ reset_config_on_routers,
+ start_topology,
+ step,
+ verify_admin_distance_for_static_routes,
+ verify_bgp_community,
+ verify_fib_routes,
+ verify_rib,
+ write_test_footer,
+ write_test_header
+)
+
+pytestmark = [pytest.mark.bgpd]
+# Global variables
+NETWORK1_1 = {"ipv4": "1.1.1.1/32", "ipv6": "1::1/128"}
+NETWORK1_2 = {"ipv4": "1.1.1.2/32", "ipv6": "1::2/128"}
+NETWORK2_1 = {"ipv4": "2.1.1.1/32", "ipv6": "2::1/128"}
+NETWORK2_2 = {"ipv4": "2.1.1.2/32", "ipv6": "2::2/128"}
+NETWORK3_1 = {"ipv4": "3.1.1.1/32", "ipv6": "3::1/128"}
+NETWORK3_2 = {"ipv4": "3.1.1.2/32", "ipv6": "3::2/128"}
+NETWORK4_1 = {"ipv4": "4.1.1.1/32", "ipv6": "4::1/128"}
+NETWORK4_2 = {"ipv4": "4.1.1.2/32", "ipv6": "4::2/128"}
+NETWORK5_1 = {"ipv4": "5.1.1.1/32", "ipv6": "5::1/128"}
+NETWORK5_2 = {"ipv4": "5.1.1.2/32", "ipv6": "5::2/128"}
+
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+
+## File name
+TCPDUMP_FILE="test_tcp_packet_test.txt"
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo,TCPDUMP_FILE
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ step("Testsuite start time: {}".format(testsuite_run_time))
+ step("=" * 40)
+
+ step("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/bgp_vrf_tcp_mss.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ global ADDR_TYPES
+ global BGP_CONVERGENCE
+ 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
+ )
+
+ step("Running setup_module() done")
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ step("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ step(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ step("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+def test_bgp_vrf_tcp_mss(request):
+ tgen = get_topogen()
+ tc_name = request.node.name
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Verify the router failures")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Configuring 5 static Routes in Router R3 with NULL0 as Next hop")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ result = create_static_routes(tgen, static_routes_input)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the static Routes in R3 on default VRF")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify the static Routes in R2 on default VRF")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ },
+ ]
+ }
+ }
+ dut = "r2"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("importing default vrf on R2 under VRF RED Address Family")
+ for addr_type in ADDR_TYPES:
+ input_import_vrf = {
+ "r2": {
+ "bgp": [
+ {
+ "local_as": 200,
+ "vrf": "RED",
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "default"}}}
+ },
+ }
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_import_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify the static Routes in R2 on RED VRF")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+ dut = "r2"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify the static Routes in R1 on RED VRF")
+ for addr_type in ADDR_TYPES:
+ static_routes_input = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]] + [NETWORK1_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK2_1[addr_type]] + [NETWORK2_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK3_1[addr_type]] + [NETWORK3_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK4_1[addr_type]] + [NETWORK4_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ {
+ "network": [NETWORK5_1[addr_type]] + [NETWORK5_2[addr_type]],
+ "next_hop": NEXT_HOP_IP[addr_type],
+ "vrf": "RED",
+ },
+ ]
+ }
+ }
+ dut = "r1"
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes_input)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Enabling tcp-mss 150 on Router R1 in VRF RED")
+ TCP_MSS = 150
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {} vrf {}".format(
+ topo["routers"]["r1"]["bgp"][0]["local_as"],
+ topo["routers"]["r1"]["bgp"][0]["vrf"],
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r1-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r1-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Clearing BGP on R1 and R2 ")
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, addr_type, "r1", vrf=topo["routers"]["r1"]["bgp"][0]["vrf"])
+ clear_bgp(tgen, addr_type, "r2", vrf=topo["routers"]["r2"]["bgp"][1]["vrf"])
+
+ step("Verify the BGP Convergence at R1 & R2 after Clear BGP")
+ r1_convergence = verify_bgp_convergence(tgen, topo, dut="r1")
+ assert (
+ r1_convergence is True
+ ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r1_convergence)
+ r2_convergence = verify_bgp_convergence(tgen, topo, dut="r2")
+ assert (
+ r2_convergence is True
+ ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence)
+
+ step("Verify the TCP-MSS value on both Router R1 and R2")
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0],
+ TCP_MSS,
+ "RED",
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+
+
+
+
+ step("Enabling tcp-mss 500 between R2 and R3 of VRF Default")
+ TCP_MSS = 500
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ },
+ "r3": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ },
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+
+
+ step("Clear BGP at router R2 and R3")
+ for addr_type in ADDR_TYPES:
+ clear_bgp(tgen, topo, "r2", addr_type)
+ clear_bgp(tgen, topo, "r3", addr_type)
+
+ step("Verify the BGP Convergence at R2 & R3 after Clear BGP")
+ r1_convergence = verify_bgp_convergence(tgen, topo, dut="r2")
+ assert (
+ r1_convergence is True
+ ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence)
+ r2_convergence = verify_bgp_convergence(tgen, topo, dut="r3")
+ assert (
+ r2_convergence is True
+ ), "BGP convergence after Clear BGP :Failed \n Error: {}".format(r2_convergence)
+
+ step("Verify the TCP-MSS value on both Router R2 and R3")
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+
+ dut = "r3"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+
+ step("Removing tcp-mss 150 between R1 and R2 of VRF RED ")
+ TCP_MSS = 150
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "router bgp {} vrf {}".format(
+ topo["routers"]["r1"]["bgp"][0]["local_as"],
+ topo["routers"]["r1"]["bgp"][0]["vrf"],
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r1-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r1-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {} vrf {}".format(
+ topo["routers"]["r2"]["bgp"][0]["local_as"],
+ topo["routers"]["r2"]["bgp"][1]["vrf"],
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify the TCP-MSS value cleared on both Router R1 and R2")
+ for addr_type in ADDR_TYPES:
+ dut = "r1"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r1-link1"][addr_type].split("/")[0],
+ TCP_MSS,
+ "RED",
+ )
+ assert (
+ tcp_mss_result is not True
+ ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result)
+
+ dut = "r2"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ "RED",
+ )
+ assert (
+ tcp_mss_result is not True
+ ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result)
+
+
+ step("Removing tcp-mss 500 between R2 and R3 of VRF Default ")
+ TCP_MSS = 500
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "no neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify the TCP-MSS value got cleared on both Router R2 and R3")
+ for addr_type in ADDR_TYPES:
+ dut = "r2"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert (
+ tcp_mss_result is not True
+ ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result)
+
+ dut = "r3"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert (
+ tcp_mss_result is not True
+ ), " TCP-MSS mismatch :Failed \n Error: {}".format(tcp_mss_result)
+
+ step("Configuring different TCP-MSS R2 and R3 ")
+ TCP_MSS = 500
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {}".format(topo["routers"]["r2"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ TCP_MSS = 300
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify the TCP-MSS value on both Router R2 and R3")
+ for addr_type in ADDR_TYPES:
+ TCP_MSS = 500
+ dut = "r2"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+
+ TCP_MSS = 300
+ dut = "r3"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+
+ step("Configure TCP_MSS > MTU on R2 and R3 and it should be 1460 ")
+ TCP_MSS = 4096
+ REF_TCP_MSS = 1460
+ raw_config = {
+ "r2": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r2"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp {} ".format(topo["routers"]["r3"]["bgp"][0]["local_as"]),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ ),
+ "neighbor {} tcp-mss {}".format(
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv6"].split("/")[0],
+ TCP_MSS,
+ ),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Restarting BGP Daemon on R3")
+
+ kill_router_daemons(tgen, "r3", ["bgpd"])
+ start_router_daemons(tgen, "r3", ["bgpd"])
+
+ step(
+ "Verify the configured TCP-MSS 4096 value on restarting both Daemon both Router R2 and R3 "
+ )
+ for addr_type in ADDR_TYPES:
+ TCP_MSS = 4096
+ dut = "r2"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r3"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+ dut = "r3"
+ tcp_mss_result = verify_tcp_mss(
+ tgen,
+ dut,
+ topo["routers"]["r2"]["links"]["r3-link1"]["ipv4"].split("/")[0],
+ TCP_MSS,
+ )
+ assert tcp_mss_result is True, " TCP-MSS mismatch :Failed \n Error: {}".format(
+ tcp_mss_result
+ )
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_update_delay/__init__.py b/tests/topotests/bgp_update_delay/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/__init__.py
diff --git a/tests/topotests/bgp_update_delay/r1/bgpd.conf b/tests/topotests/bgp_update_delay/r1/bgpd.conf
new file mode 100644
index 0000000..8ebb509
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r1/bgpd.conf
@@ -0,0 +1,10 @@
+! exit1
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.255.1 remote-as 65002
+ neighbor 192.168.255.1 timers connect 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_update_delay/r1/zebra.conf b/tests/topotests/bgp_update_delay/r1/zebra.conf
new file mode 100644
index 0000000..9904bb4
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r1/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.255.254/32
+!
+interface r1-eth0
+ ip address 192.168.255.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_update_delay/r2/bgpd.conf b/tests/topotests/bgp_update_delay/r2/bgpd.conf
new file mode 100644
index 0000000..438f995
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r2/bgpd.conf
@@ -0,0 +1,18 @@
+! spine
+router bgp 65002
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.255.2 remote-as 65001
+ neighbor 192.168.254.2 remote-as 65003
+ neighbor 192.168.253.2 remote-as 65004
+ neighbor 192.168.255.2 timers connect 10
+ neighbor 192.168.254.2 timers connect 10
+ neighbor 192.168.253.2 timers connect 10
+!
+ router bgp 65002 vrf vrf1
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.252.2 remote-as 65005
+ neighbor 192.168.252.2 timers connect 10
+ !
+!
diff --git a/tests/topotests/bgp_update_delay/r2/zebra.conf b/tests/topotests/bgp_update_delay/r2/zebra.conf
new file mode 100644
index 0000000..420f00d
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r2/zebra.conf
@@ -0,0 +1,20 @@
+! spine
+interface r2-eth0
+ ip address 192.168.255.1/30
+!
+interface r2-eth1
+ ip address 192.168.254.1/30
+!
+interface r2-eth2
+ ip address 192.168.253.1/30
+!
+interface r2-eth3
+ ip address 192.168.252.1/30
+ vrf vrf1
+!
+auto vrf1
+iface vrf1
+ vrf-table auto
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_update_delay/r3/bgpd.conf b/tests/topotests/bgp_update_delay/r3/bgpd.conf
new file mode 100644
index 0000000..53e5178
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r3/bgpd.conf
@@ -0,0 +1,10 @@
+! exit2
+router bgp 65003
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.254.1 remote-as 65002
+ neighbor 192.168.254.1 timers connect 10
+ address-family ipv4 unicast
+ redistribute connected
+ !
+!
diff --git a/tests/topotests/bgp_update_delay/r3/zebra.conf b/tests/topotests/bgp_update_delay/r3/zebra.conf
new file mode 100644
index 0000000..f490d97
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r3/zebra.conf
@@ -0,0 +1,9 @@
+! exit2
+interface lo
+ ip address 172.16.254.254/32
+!
+interface r3-eth0
+ ip address 192.168.254.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_update_delay/r4/bgpd.conf b/tests/topotests/bgp_update_delay/r4/bgpd.conf
new file mode 100644
index 0000000..34cb429
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r4/bgpd.conf
@@ -0,0 +1,11 @@
+! exit2
+router bgp 65004
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.253.1 remote-as 65002
+ neighbor 192.168.253.1 timers connect 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_update_delay/r4/zebra.conf b/tests/topotests/bgp_update_delay/r4/zebra.conf
new file mode 100644
index 0000000..baba04c
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r4/zebra.conf
@@ -0,0 +1,9 @@
+! exit2
+interface lo
+ ip address 172.16.253.254/32
+!
+interface r4-eth0
+ ip address 192.168.253.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_update_delay/r5/bgpd.conf b/tests/topotests/bgp_update_delay/r5/bgpd.conf
new file mode 100644
index 0000000..66ecc70
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r5/bgpd.conf
@@ -0,0 +1,11 @@
+! exit1
+router bgp 65005
+ no bgp ebgp-requires-policy
+ timers bgp 3 9
+ neighbor 192.168.252.1 remote-as 65002
+ neighbor 192.168.252.1 timers connect 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_update_delay/r5/zebra.conf b/tests/topotests/bgp_update_delay/r5/zebra.conf
new file mode 100644
index 0000000..8adf6f8
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/r5/zebra.conf
@@ -0,0 +1,9 @@
+! exit1
+interface lo
+ ip address 172.16.252.254/32
+!
+interface r1-eth0
+ ip address 192.168.252.2/30
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_update_delay/test_bgp_update_delay.py b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
new file mode 100644
index 0000000..1c00c49
--- /dev/null
+++ b/tests/topotests/bgp_update_delay/test_bgp_update_delay.py
@@ -0,0 +1,308 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_update_delay.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by
+# Don Slice <dslice@nvidia.com>
+#
+# 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 the ability to define update-delay to delay bestpath, rib install
+and advertisement to peers when frr is started, restarted or "clear ip
+bgp *" is performed. Test both the vrf-specific and global configuration
+and operation.
+
+r1
+|
+r2----r3
+| \
+| \
+r5 r4
+
+
+r2 is UUT and peers with r1, r3, and r4 in default bgp instance.
+r2 peers with r5 in vrf vrf1.
+
+Check r2 initial convergence in default table
+Define update-delay with max-delay in the default bgp instance on r2
+Shutdown peering on r1 toward r2 so that delay timers can be exercised
+Clear bgp neighbors on r2 and then check for the 'in progress' indicator
+Check that r2 only installs route learned from r4 after the max-delay timer expires
+Define update-delay with max-delay and estabish-wait and check json output showing set
+Clear neighbors on r2 and check that r3 installs route from r4 after establish-wait time
+Remove update-delay timer on r2 to verify that it goes back to normal behavior
+Clear neighbors on r2 and check that route install time on r2 does not delay
+Define global bgp update-delay with max-delay and establish-wait on r2
+Check that r2 default instance and vrf1 have the max-delay and establish set
+Clear neighbors on r2 and check route-install time is after the establish-wait timer
+
+Note that the keepalive/hold times were changed to 3/9 and the connect retry timer
+to 10 to improve the odds the convergence timing in this test case is useful in the
+event of packet loss.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd]
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ for routern in range(1, 6):
+ 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r5"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_update_delay():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+ router3 = tgen.gears["r3"]
+
+ # initial convergence without update-delay defined
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp neighbor 192.168.255.2 json"))
+ expected = {
+ "192.168.255.2": {
+ "bgpState": "Established",
+ "addressFamilyInfo": {"ipv4Unicast": {"acceptedPrefixCounter": 2}},
+ }
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_update_delay(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp sum json"))
+ expected = {"ipv4Unicast": {"updateDelayLimit": 20}}
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_update_delay_in_progress(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp sum json"))
+ expected = {"ipv4Unicast": {"updateDelayInProgress": True}}
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_route_install(router):
+ output = json.loads(router.vtysh_cmd("show ip route 172.16.253.254/32 json"))
+ expected = {"172.16.253.254/32": [{"protocol": "bgp"}]}
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_update_delay_and_wait(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp sum json"))
+ expected = {
+ "ipv4Unicast": {"updateDelayLimit": 20, "updateDelayEstablishWait": 10}
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_update_delay(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp sum json"))
+ expected = {"ipv4Unicast": {"updateDelayLimit": 20}}
+
+ return topotest.json_cmp(output, expected)
+
+ def _bgp_check_vrf_update_delay_and_wait(router):
+ output = json.loads(router.vtysh_cmd("show ip bgp vrf vrf1 sum json"))
+ expected = {
+ "ipv4Unicast": {"updateDelayLimit": 20, "updateDelayEstablishWait": 10}
+ }
+
+ return topotest.json_cmp(output, expected)
+
+ # Check r2 initial convergence in default table
+ test_func = functools.partial(_bgp_converge, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed bgp convergence in "{}"'.format(router2)
+
+ # Define update-delay with max-delay in the default bgp instance on r2
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ update-delay 20
+ """
+ )
+
+ # Shutdown peering on r1 toward r2 so that delay timers can be exercised
+ router1.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65001
+ neighbor 192.168.255.1 shut
+ """
+ )
+
+ # Clear bgp neighbors on r2 and then check for the 'in progress' indicator
+ router2.vtysh_cmd("""clear ip bgp *""")
+
+ test_func = functools.partial(_bgp_check_update_delay_in_progress, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed to set update-delay max-delay timer "{}"'.format(
+ router2
+ )
+
+ # Check that r2 only installs route learned from r4 after the max-delay timer expires
+ test_func = functools.partial(_bgp_check_route_install, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed to install route after update-delay "{}"'.format(
+ router2
+ )
+
+ # Define update-delay with max-delay and estabish-wait and check json output showing set
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ update-delay 20 10
+ """
+ )
+
+ test_func = functools.partial(_bgp_check_update_delay_and_wait, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert (
+ result is None
+ ), 'Failed to set max-delay and establish-weight timers in "{}"'.format(router2)
+
+ # Define update-delay with max-delay and estabish-wait and check json output showing set
+ router2.vtysh_cmd("""clear ip bgp *""")
+
+ test_func = functools.partial(_bgp_check_route_install, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert (
+ result is None
+ ), 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(
+ router2
+ )
+
+ # Remove update-delay timer on r2 to verify that it goes back to normal behavior
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ router bgp 65002
+ no update-delay
+ """
+ )
+
+ # Clear neighbors on r2 and check that route install time on r2 does not delay
+ router2.vtysh_cmd("""clear ip bgp *""")
+
+ test_func = functools.partial(_bgp_check_route_install, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed to remove update-delay delay timing "{}"'.format(
+ router2
+ )
+
+ # Define global bgp update-delay with max-delay and establish-wait on r2
+ router2.vtysh_cmd(
+ """
+ configure terminal
+ bgp update-delay 20 10
+ """
+ )
+
+ # Check that r2 default instance and vrf1 have the max-delay and establish set
+ test_func = functools.partial(_bgp_check_update_delay_and_wait, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed to set update-delay in default instance "{}"'.format(
+ router2
+ )
+
+ test_func = functools.partial(_bgp_check_vrf_update_delay_and_wait, router2)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert result is None, 'Failed to set update-delay in vrf1 "{}"'.format(router2)
+
+ # Clear neighbors on r2 and check route-install time is after the establish-wait timer
+ router2.vtysh_cmd("""clear ip bgp *""")
+
+ test_func = functools.partial(_bgp_check_route_install, router3)
+ success, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+
+ assert (
+ result is None
+ ), 'Failed to installed advertised route after establish-wait timer espired "{}"'.format(
+ router2
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vpnv4_ebgp/__init__.py b/tests/topotests/bgp_vpnv4_ebgp/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/__init__.py
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf
new file mode 100644
index 0000000..2eebe5e
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65500
+ bgp router-id 1.1.1.1
+ no bgp ebgp-requires-policy
+ neighbor 10.125.0.2 remote-as 65501
+ address-family ipv4 unicast
+ no neighbor 10.125.0.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 10.125.0.2 activate
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 1.1.1.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 101
+ rd vpn export 444:1
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r1-eth0
+ mpls bgp forwarding
+! \ No newline at end of file
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json
new file mode 100644
index 0000000..da7d281
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/ipv4_routes.json
@@ -0,0 +1,50 @@
+{
+ "10.200.0.0/24": [
+ {
+ "prefix": "10.200.0.0/24",
+ "prefixLen": 24,
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "10.125.0.2",
+ "afi": "ipv4",
+ "interfaceName": "r1-eth0",
+ "vrf": "default",
+ "active": true,
+ "labels":[
+ 102
+ ]
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "prefix": "10.201.0.0/24",
+ "prefixLen": 24,
+ "protocol": "connected",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops":[
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "r1-eth1",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf
new file mode 100644
index 0000000..e9ae4e9
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r1/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r1-eth1 vrf vrf1
+ ip address 10.201.0.1/24
+!
+interface r1-eth0
+ ip address 10.125.0.1/24
+!
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json
new file mode 100644
index 0000000..19797dd
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgp_ipv4_routes.json
@@ -0,0 +1,38 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65501,
+ "routes":
+ {
+ "10.201.0.0/24": [
+ {
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0\/24",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "10.125.0.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.200.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10.200.0.0",
+ "prefixLen": 24,
+ "network": "10.200.0.0\/24",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf
new file mode 100644
index 0000000..e38c99d
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/bgpd.conf
@@ -0,0 +1,25 @@
+router bgp 65501
+ bgp router-id 2.2.2.2
+ no bgp ebgp-requires-policy
+ neighbor 10.125.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 10.125.0.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 10.125.0.1 activate
+ exit-address-family
+!
+router bgp 65501 vrf vrf1
+ bgp router-id 2.2.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:2
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+interface r2-eth0
+ mpls bgp forwarding
+!
diff --git a/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf
new file mode 100644
index 0000000..6c433ae
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/r2/zebra.conf
@@ -0,0 +1,7 @@
+log stdout
+interface r2-eth1 vrf vrf1
+ ip address 10.200.0.2/24
+!
+interface r2-eth0
+ ip address 10.125.0.2/24
+!
diff --git a/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py
new file mode 100644
index 0000000..cd8a9b6
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_ebgp/test_bgp_vpnv4_ebgp.py
@@ -0,0 +1,187 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_vpnv4_ebgp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_vpnv4_ebgp.py: Test the FRR BGP daemon with EBGP direct connection
+"""
+
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ 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["r1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+def _populate_iface():
+ tgen = get_topogen()
+ cmds_list = [
+ 'ip link add vrf1 type vrf table 10',
+ 'echo 100000 > /proc/sys/net/mpls/platform_labels',
+ 'ip link set dev vrf1 up',
+ 'ip link set dev {0}-eth1 master vrf1',
+ 'echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input',
+ ]
+
+ for cmd in cmds_list:
+ input = cmd.format('r1', '1', '2')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2'))
+ logger.info('output: ' + output)
+
+ for cmd in cmds_list:
+ input = cmd.format('r2', '2', '1')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1'))
+ logger.info('output: ' + output)
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ _populate_iface()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears['r1']
+ logger.info("Dump some context for r1")
+ router.vtysh_cmd("show bgp ipv4 vpn")
+ router.vtysh_cmd("show bgp summary")
+ router.vtysh_cmd("show bgp vrf vrf1 ipv4")
+ router.vtysh_cmd("show running-config")
+ router = tgen.gears['r2']
+ logger.info("Dump some context for r2")
+ router.vtysh_cmd("show bgp ipv4 vpn")
+ router.vtysh_cmd("show bgp summary")
+ router.vtysh_cmd("show bgp vrf vrf1 ipv4")
+ router.vtysh_cmd("show running-config")
+
+ # Check IPv4 routing tables on r1
+ logger.info("Checking IPv4 routes for convergence on r1")
+ router = tgen.gears['r1']
+ json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ assert 0, 'ipv4_routes.json file not found'
+ return
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route vrf vrf1 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check BGP IPv4 routing tables on r2 not installed
+ logger.info("Checking BGP IPv4 routes for convergence on r2")
+ router = tgen.gears['r2']
+ json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ assert 0, 'bgp_ipv4_routes.json file not found'
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp vrf vrf1 ipv4 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+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_vpnv4_gre/__init__.py b/tests/topotests/bgp_vpnv4_gre/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/__init__.py
diff --git a/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf
new file mode 100644
index 0000000..0e2d3a8
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r1/bgpd.conf
@@ -0,0 +1,26 @@
+router bgp 65500
+ bgp router-id 192.0.2.1
+ neighbor 192.0.2.2 remote-as 65500
+ neighbor 192.0.2.2 update-source 192.0.2.1
+ address-family ipv4 unicast
+ no neighbor 192.0.2.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.2 activate
+ neighbor 192.0.2.2 route-map rmap in
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 192.0.2.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 101
+ rd vpn export 444:1
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+route-map rmap permit 1
+ set l3vpn next-hop encapsulation gre
+!
diff --git a/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json
new file mode 100644
index 0000000..5f2732a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r1/ipv4_routes.json
@@ -0,0 +1,50 @@
+{
+ "10.200.0.0/24": [
+ {
+ "prefix": "10.200.0.0/24",
+ "prefixLen": 24,
+ "protocol": "bgp",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "ip": "192.168.0.2",
+ "afi": "ipv4",
+ "interfaceName": "r1-gre0",
+ "vrf": "default",
+ "active": true,
+ "labels":[
+ 102
+ ]
+ }
+ ]
+ }
+ ],
+ "10.201.0.0/24": [
+ {
+ "prefix": "10.201.0.0/24",
+ "prefixLen": 24,
+ "protocol": "connected",
+ "vrfName": "vrf1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops":[
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "r1-eth1",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf
new file mode 100644
index 0000000..11780a8
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r1/zebra.conf
@@ -0,0 +1,14 @@
+log stdout
+ip route 192.0.2.2/32 192.168.0.2
+interface lo
+ ip address 192.0.2.1/32
+!
+interface r1-gre0
+ ip address 192.168.0.1/24
+!
+interface r1-eth1 vrf vrf1
+ ip address 10.201.0.1/24
+!
+interface r1-eth0
+ ip address 10.125.0.1/24
+!
diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json
new file mode 100644
index 0000000..e50d5dd
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r2/bgp_ipv4_routes.json
@@ -0,0 +1,38 @@
+{
+ "vrfName": "vrf1",
+ "localAS": 65500,
+ "routes":
+ {
+ "10.201.0.0/24": [
+ {
+ "prefix": "10.201.0.0",
+ "prefixLen": 24,
+ "network": "10.201.0.0\/24",
+ "nhVrfName": "default",
+ "nexthops": [
+ {
+ "ip": "192.0.2.1",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ],
+ "10.200.0.0/24": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "prefix": "10.200.0.0",
+ "prefixLen": 24,
+ "network": "10.200.0.0\/24",
+ "nexthops": [
+ {
+ "ip": "0.0.0.0",
+ "afi": "ipv4",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf
new file mode 100644
index 0000000..bf05866
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r2/bgpd.conf
@@ -0,0 +1,22 @@
+router bgp 65500
+ bgp router-id 192.0.2.2
+ neighbor 192.0.2.1 remote-as 65500
+ neighbor 192.0.2.1 update-source 192.0.2.2
+ address-family ipv4 unicast
+ no neighbor 192.0.2.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 192.0.2.1 activate
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 192.0.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:2
+ rt vpn both 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf
new file mode 100644
index 0000000..de88a4b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/r2/zebra.conf
@@ -0,0 +1,14 @@
+log stdout
+ip route 192.0.2.1/32 192.168.0.1
+interface lo
+ ip address 192.0.2.2/32
+!
+interface r2-gre0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1 vrf vrf1
+ ip address 10.200.0.2/24
+!
+interface r2-eth0
+ ip address 10.125.0.2/24
+!
diff --git a/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py
new file mode 100644
index 0000000..f562f44
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_gre/test_bgp_vpnv4_gre.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_vpnv4_gre.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_vpnv4_gre.py: Test the FRR BGP daemon with BGP IPv6 interface
+ with route advertisements on a separate netns.
+"""
+
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ 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["r1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+def _populate_iface():
+ tgen = get_topogen()
+ cmds_list = [
+ 'ip link add vrf1 type vrf table 10',
+ 'echo 10 > /proc/sys/net/mpls/platform_labels',
+ 'ip link set dev vrf1 up',
+ 'ip link set dev {0}-eth1 master vrf1',
+ 'echo 1 > /proc/sys/net/mpls/conf/{0}-eth0/input',
+ 'ip tunnel add {0}-gre0 mode gre ttl 64 dev {0}-eth0 local 10.125.0.{1} remote 10.125.0.{2}',
+ 'ip link set dev {0}-gre0 up',
+ 'echo 1 > /proc/sys/net/mpls/conf/{0}-gre0/input',
+ ]
+
+ for cmd in cmds_list:
+ input = cmd.format('r1', '1', '2')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2'))
+ logger.info('output: ' + output)
+
+ for cmd in cmds_list:
+ input = cmd.format('r2', '2', '1')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1'))
+ logger.info('output: ' + output)
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ _populate_iface()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears['r1']
+ logger.info("Dump some context for r1")
+ router.vtysh_cmd("show bgp ipv4 vpn")
+ router.vtysh_cmd("show bgp summary")
+ router.vtysh_cmd("show bgp vrf vrf1 ipv4")
+ router.vtysh_cmd("show running-config")
+ router = tgen.gears['r2']
+ logger.info("Dump some context for r2")
+ router.vtysh_cmd("show bgp ipv4 vpn")
+ router.vtysh_cmd("show bgp summary")
+ router.vtysh_cmd("show bgp vrf vrf1 ipv4")
+ router.vtysh_cmd("show running-config")
+
+ # Check IPv4 routing tables on r1
+ logger.info("Checking IPv4 routes for convergence on r1")
+ router = tgen.gears['r1']
+ json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ assert 0, 'ipv4_routes.json file not found'
+ return
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route vrf vrf1 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check BGP IPv4 routing tables on r2 not installed
+ logger.info("Checking BGP IPv4 routes for convergence on r2")
+ router = tgen.gears['r2']
+ json_file = "{}/{}/bgp_ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ assert 0, 'bgp_ipv4_routes.json file not found'
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp vrf vrf1 ipv4 json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+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_vpnv4_noretain/__init__.py b/tests/topotests/bgp_vpnv4_noretain/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/__init__.py
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf
new file mode 100644
index 0000000..3d8773b
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/bgpd.conf
@@ -0,0 +1,24 @@
+router bgp 65500
+ bgp router-id 1.1.1.1
+ neighbor 10.125.0.2 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 10.125.0.2 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 10.125.0.2 activate
+ no bgp retain route-target all
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 1.1.1.1
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 101
+ rd vpn export 444:1
+ rt vpn import 51:100 52:100
+ rt vpn export 51:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json
new file mode 100644
index 0000000..903c460
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes.json
@@ -0,0 +1,69 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "tableVersion":1,
+ "routerId":"1.1.1.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "444:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "version":1,
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "444:2":{
+ "10.200.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.200.0.0",
+ "prefixLen":24,
+ "network":"10.200.0.0\/24",
+ "version":1,
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "444:3":{
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json
new file mode 100644
index 0000000..3cc0b4a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/ipv4_vpn_routes_unfiltered.json
@@ -0,0 +1,94 @@
+{
+ "vrfId":0,
+ "vrfName":"default",
+ "tableVersion":1,
+ "routerId":"1.1.1.1",
+ "defaultLocPrf":100,
+ "localAS":65500,
+ "routes":{
+ "routeDistinguishers":{
+ "444:1":{
+ "10.201.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"external",
+ "prefix":"10.201.0.0",
+ "prefixLen":24,
+ "network":"10.201.0.0\/24",
+ "version":1,
+ "metric":0,
+ "weight":32768,
+ "peerId":"(unspec)",
+ "path":"",
+ "origin":"incomplete",
+ "announceNexthopSelf":true,
+ "nhVrfName":"vrf1",
+ "nexthops":[
+ {
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "444:2":{
+ "10.200.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.200.0.0",
+ "prefixLen":24,
+ "network":"10.200.0.0\/24",
+ "version":1,
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ },
+ "444:3":{
+ "10.210.0.0/24":[
+ {
+ "valid":true,
+ "bestpath":true,
+ "selectionReason":"First path received",
+ "pathFrom":"internal",
+ "prefix":"10.210.0.0",
+ "prefixLen":24,
+ "network":"10.210.0.0\/24",
+ "version":1,
+ "metric":0,
+ "locPrf":100,
+ "weight":0,
+ "peerId":"10.125.0.2",
+ "path":"",
+ "origin":"incomplete",
+ "nexthops":[
+ {
+ "ip":"10.125.0.2",
+ "afi":"ipv4",
+ "used":true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf b/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf
new file mode 100644
index 0000000..6f5cb6e
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/isisd.conf
@@ -0,0 +1,14 @@
+interface r1-eth0
+ ip router isis 1
+ isis circuit-type level-1
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+router isis 1
+ is-type level-1
+ net 49.0002.0000.1994.00
+ segment-routing on
+ segment-routing prefix 1.1.1.1/32 index 11
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf
new file mode 100644
index 0000000..5b8b1e8
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r1/zebra.conf
@@ -0,0 +1,13 @@
+log stdout
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-gre0
+ ip address 192.168.0.1/24
+!
+interface r1-eth1 vrf vrf1
+ ip address 10.201.0.1/24
+!
+interface r1-eth0
+ ip address 10.125.0.1/24
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf
new file mode 100644
index 0000000..235fb31
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/bgpd.conf
@@ -0,0 +1,35 @@
+router bgp 65500
+ bgp router-id 2.2.2.2
+ neighbor 10.125.0.1 remote-as 65500
+ address-family ipv4 unicast
+ no neighbor 10.125.0.1 activate
+ exit-address-family
+ address-family ipv4 vpn
+ neighbor 10.125.0.1 activate
+ no bgp retain route-target all
+ exit-address-family
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 2.2.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:2
+ rt vpn import 53:100 52:100 51:100
+ rt vpn export 52:100
+ export vpn
+ import vpn
+ exit-address-family
+!
+router bgp 65500 vrf vrf2
+ bgp router-id 2.2.2.2
+ address-family ipv4 unicast
+ redistribute connected
+ label vpn export 102
+ rd vpn export 444:3
+ rt vpn both 53:100 52:100 51:100
+ rt vpn both 53:100
+ export vpn
+ import vpn
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf b/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf
new file mode 100644
index 0000000..cbec8c3
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/isisd.conf
@@ -0,0 +1,14 @@
+interface r2-eth0
+ ip router isis 1
+ isis circuit-type level-1
+!
+interface lo
+ ip router isis 1
+ isis passive
+!
+router isis 1
+ is-type level-1
+ net 49.0002.0000.1995.00
+ segment-routing on
+ segment-routing prefix 2.2.2.2/32 index 22
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf
new file mode 100644
index 0000000..7ec644a
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/r2/zebra.conf
@@ -0,0 +1,16 @@
+log stdout
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-gre0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1 vrf vrf1
+ ip address 10.200.0.2/24
+!
+interface r2-eth2 vrf vrf2
+ ip address 10.210.0.2/24
+!
+interface r2-eth0
+ ip address 10.125.0.2/24
+!
diff --git a/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py
new file mode 100644
index 0000000..b4a841d
--- /dev/null
+++ b/tests/topotests/bgp_vpnv4_noretain/test_bgp_vpnv4_noretain.py
@@ -0,0 +1,201 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_vpnv4_noretain.py
+# Part of NetDEF Topology Tests
+#
+# Copyright 2022 6WIND S.A.
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_vpnv4_noretain.py: Do not keep the VPNvx entries when no
+ VRF matches incoming VPNVx entries
+"""
+
+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.
+
+
+pytestmark = [pytest.mark.bgpd]
+
+def build_topo(tgen):
+ "Build function"
+
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ 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["r1"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r2"])
+
+
+def _populate_iface():
+ tgen = get_topogen()
+ cmds_list = [
+ 'modprobe mpls_router',
+ 'echo 100000 > /proc/sys/net/mpls/platform_labels',
+ 'ip link add vrf1 type vrf table 10',
+ 'ip link set dev vrf1 up',
+ 'ip link set dev {0}-eth1 master vrf1',
+ 'echo 1 > /proc/sys/net/mpls/conf/vrf1/input',
+ ]
+ cmds_list_extra = [
+ 'ip link add vrf2 type vrf table 20',
+ 'ip link set dev vrf2 up',
+ 'ip link set dev {0}-eth2 master vrf2',
+ 'echo 1 > /proc/sys/net/mpls/conf/vrf2/input',
+ ]
+
+ for cmd in cmds_list:
+ input = cmd.format('r1', '1', '2')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r1'].cmd(cmd.format('r1', '1', '2'))
+ logger.info('output: ' + output)
+
+ for cmd in cmds_list:
+ input = cmd.format('r2', '2', '1')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1'))
+ logger.info('output: ' + output)
+
+ for cmd in cmds_list_extra:
+ input = cmd.format('r2', '2', '1')
+ logger.info('input: ' + cmd)
+ output = tgen.net['r2'].cmd(cmd.format('r2', '2', '1'))
+ logger.info('output: ' + output)
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ _populate_iface()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv4 VPN routing tables on r1
+ logger.info("Checking IPv4 routes for convergence on r1")
+ router = tgen.gears['r1']
+ json_file = "{}/{}/ipv4_vpn_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ assert 0, 'ipv4_vpn_routes.json file not found'
+ return
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp ipv4 vpn json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check BGP IPv4 routing tables after unsetting no retain flag
+ logger.info("Checking BGP IPv4 routes for convergence on r2")
+ router = tgen.gears['r1']
+ router.vtysh_cmd("configure\nrouter bgp 65500\naddress-family ipv4 vpn\nbgp retain route-target all\n")
+
+ # Check IPv4 VPN routing tables on r1
+ logger.info("Checking IPv4 routes for convergence on r1")
+ router = tgen.gears['r1']
+ json_file = "{}/{}/ipv4_vpn_routes_unfiltered.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ assert 0, 'ipv4_vpn_routes_unfiltered.json file not found'
+ return
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp ipv4 vpn json",
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+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_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json
new file mode 100644
index 0000000..b1d7d09
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo1.json
@@ -0,0 +1,563 @@
+{
+ "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": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["11.11.11.1/32", "11.11.11.11/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["11:11::1/128", "11:11::11/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["10.10.10.10/32", "10.10.10.100/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["10:10::10/128", "10:10::100/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["22.22.22.2/32", "22.22.22.22/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["22:22::2/128", "22:22::22/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["20.20.20.20/32", "20.20.20.200/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["20:20::20/128", "20:20::200/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["30.30.30.3/32", "30.30.30.30/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["30:30::3/128", "30:30::30/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["40.40.40.4/32", "40.40.40.40/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["40:40::4/128", "40:40::40/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json
new file mode 100644
index 0000000..b1d7d09
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/bgp_vrf_dynamic_route_leak_topo2.json
@@ -0,0 +1,563 @@
+{
+ "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": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["11.11.11.1/32", "11.11.11.11/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["11:11::1/128", "11:11::11/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["10.10.10.10/32", "10.10.10.100/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["10:10::10/128", "10:10::100/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["22.22.22.2/32", "22.22.22.22/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["22:22::2/128", "22:22::22/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["20.20.20.20/32", "20.20.20.200/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["20:20::20/128", "20:20::200/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["30.30.30.3/32", "30.30.30.30/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["30:30::3/128", "30:30::30/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["40.40.40.4/32", "40.40.40.40/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["40:40::4/128", "40:40::40/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
new file mode 100644
index 0000000..7d71ef7
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo1.py
@@ -0,0 +1,1916 @@
+#!/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 BGP Multi-VRF Dynamic Route Leaking:
+
+1. Verify that dynamically imported routes are further advertised
+ to iBGP peers(peer in cluster).
+2. Verify matching a prefix based on community attribute and
+ importing it by stripping off this value
+3. Verify the route-map operation along with dynamic import command.
+4. Verifying the JSON outputs for all supported commands
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+
+# 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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ step,
+ create_route_maps,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ create_interface_in_kernel,
+ check_router_status,
+ verify_cli_json,
+ verify_fib_routes,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"}
+NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"}
+
+NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"}
+NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"}
+NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"}
+NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"}
+
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+LOOPBACK_1 = {
+ "ipv4": "10.0.0.7/24",
+ "ipv6": "fd00:0:0:1::7/64",
+}
+LOOPBACK_2 = {
+ "ipv4": "10.0.0.16/24",
+ "ipv6": "fd00:0:0:3::5/64",
+}
+PREFERRED_NEXT_HOP = "global"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def disable_route_map_to_prefer_global_next_hop(tgen, topo):
+ """
+ This API is to remove prefer global route-map applied on neighbors
+
+ Parameter:
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : Input JSON data
+
+ Returns:
+ --------
+ True/errormsg
+
+ """
+
+ logger.info("Remove prefer-global rmap applied on neighbors")
+ input_dict = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase :Failed \n Error: {}".format(result)
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_dynamic_imported_routes_advertised_to_iBGP_peer_p0(request):
+ """
+ TC5_FUNC_5:
+ 1.5.5. Verify that dynamically imported routes are further advertised
+ to iBGP peers(peer in cluster).
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Redistribute configured static routes into BGP process" " on R1 and R3/R4"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1", "r3", "r4"]
+ VRFS = ["default", "default", "default"]
+ AS_NUM = [100, 300, 400]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify that R1 receives BGP routes from R3 and R4 in " "vrf default.")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK3_1[addr_type],
+ NETWORK3_2[addr_type],
+ NETWORK3_3[addr_type],
+ NETWORK3_4[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+
+ input_routes_r4 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK4_1[addr_type],
+ NETWORK4_2[addr_type],
+ NETWORK4_3[addr_type],
+ NETWORK4_4[addr_type],
+ ]
+ }
+ ]
+ }
+ }
+
+ DUT = ["r1", "r2"]
+ INPUT_DICT = [input_routes_r3, input_routes_r4]
+
+ for dut, routes in zip(DUT, INPUT_DICT):
+ result = verify_bgp_rib(tgen, addr_type, dut, routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, dut, routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Import from default vrf into vrf ISR on R1")
+
+ input_dict_isr = {}
+ DUT = ["r1", "r2"]
+ VRFS = ["ISR", "ISR"]
+ AS_NUM = [100, 100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "default"}}}
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that default vrf's imported routes are installed "
+ "in RIB/FIB of vrf ISR on R1:"
+ )
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK3_1[addr_type],
+ NETWORK3_2[addr_type],
+ NETWORK3_3[addr_type],
+ NETWORK3_4[addr_type],
+ ],
+ "vrf": "ISR",
+ }
+ ]
+ }
+ }
+
+ input_routes_r4 = {
+ "r4": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK4_1[addr_type],
+ NETWORK4_2[addr_type],
+ NETWORK4_3[addr_type],
+ NETWORK4_4[addr_type],
+ ],
+ "vrf": "ISR",
+ }
+ ]
+ }
+ }
+
+ INPUT_DICT_VRF = [input_routes_r3, input_routes_r4]
+
+ for routes in INPUT_DICT_VRF:
+ result = verify_bgp_rib(tgen, addr_type, "r1", routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_fib_routes(tgen, addr_type, "r1", routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ intf_r2_r1 = topo["routers"]["r2"]["links"]["r1-link1"]
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Create a loopback10 interface on R1 with below IP address and "
+ "associate with vrf ISR:"
+ )
+
+ create_interface_in_kernel(
+ tgen,
+ "r1",
+ "loopback2",
+ LOOPBACK_2[addr_type],
+ "ISR",
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "On router R1 Change the next-hop of static routes in vrf "
+ "ISR to LOOPBACK_2"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]],
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_routes_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]],
+ "next_hop": (intf_r2_r1[addr_type]).split("/")[0],
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_routes_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that, though R1 originating BGP routes with next-hop"
+ " 24.1.1.2/24::1:2, which is local to R2(but in default vrf)"
+ ", R2 must receives and install all routes from R1 in vrf ISR."
+ )
+ step(
+ "Verify on R2, that it now rejects 10.10.10.x routes originated "
+ "from R1. As next-hop IP is local to R2's vrf ISR."
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]],
+ "vrf": "ISR",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes are still present \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("On router R1 delete static routes in vrf ISR to LOOPBACK_1")
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_3[addr_type], NETWORK1_4[addr_type]],
+ "next_hop": (intf_r2_r1[addr_type]).split("/")[0],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_routes_r1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request):
+ """
+ TC7_FUNC_7:
+ 1.5.7. Verify matching a prefix based on community attribute and
+ importing it by stripping off this value
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure route-map to set community attribute for a specific"
+ "prefix on R1 in vrf ISR"
+ )
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_ABC_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_cl = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": "100:100",
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_XYZ_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pflist_ABC_{}".format(addr_type)
+ }
+ },
+ "set": {"community": {"num": "100:100"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply this route-map on R1 to vrf ISR while redistributing the"
+ " prefixes into BGP"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1"]
+ VRFS = ["ISR"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure another route-map for filtering the prefixes based on"
+ " community attribute while importing into default vrf"
+ )
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": "none"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply the route-map while Importing vrf ISR's prefixes into "
+ "default vrf on router R1:"
+ )
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify on R1 that only prefixes with community value 100:100"
+ "in vrf ISR are imported to vrf default. While importing, the"
+ " community value has been stripped off:"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict_comm = {"community": "100:100"}
+
+ result = verify_bgp_community(
+ tgen,
+ addr_type,
+ dut,
+ [NETWORK1_1[addr_type]],
+ input_dict_comm,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: Commnunity is not stipped off, {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Remove/re-add route-map XYZ from redistribution.")
+
+ input_dict_1 = {}
+ DUT = ["r1"]
+ VRFS = ["ISR"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ "delete": True,
+ }
+ ]
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that all the routes disappear from vrf default when "
+ "route-map is removed from redistribution, and appear again "
+ "when route-map is re-added to redistribution in vrf ISR."
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict_1 = {}
+ DUT = ["r1"]
+ VRFS = ["ISR"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Remove/re-add route-map IMP form import statement.")
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type),
+ "delete": True,
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that when route-map IMP is removed all the prefixes of"
+ " vrf ISR are imported to vrf default. However when route-map "
+ "IMP is re-added only 11.11.11.1 and 11:11::1 (with community "
+ "value) are imported."
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Delete/Re-add prefix-list ABC.")
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_ABC_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ input_dict_pf["r1"]["prefix_lists"][addr_type][
+ "pflist_ABC_{}".format(addr_type)
+ ][0]["delete"] = False
+
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Delete/Re-add community-list COMM.")
+
+ input_dict_cl = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": "100:100",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ input_dict_cl["r1"]["bgp_community_lists"][0]["delete"] = False
+
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Delete/Re-add route-map XYZ.")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_XYZ_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pflist_ABC_{}".format(addr_type)
+ }
+ },
+ "set": {"community": {"num": "100:100"}},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ input_dict_rm["r1"]["route_maps"]["rmap_XYZ_{}".format(addr_type)][0][
+ "delete"
+ ] = False
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Delete/Re-add route-map IMP.")
+
+ input_dict_rm2 = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": "none"}},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ input_dict_rm2["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0][
+ "delete"
+ ] = False
+
+ result = create_route_maps(tgen, input_dict_rm2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_routemap_operatons_with_dynamic_import_p0(request):
+ """
+ TC8_FUNC_8:
+ 1.5.8. Verify the route-map operation along with dynamic import command.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure route-map to set community attribute for a specific"
+ "prefix on R1 in vrf ISR"
+ )
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_ABC_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_cl = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": "100:100",
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_XYZ_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pflist_ABC_{}".format(addr_type)
+ }
+ },
+ "set": {"community": {"num": "100:100"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply this route-map on R1 to vrf ISR while redistributing the"
+ " prefixes into BGP"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1"]
+ VRFS = ["ISR"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure another route-map for filtering the prefixes based on"
+ " community attribute while importing into default vrf"
+ )
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": "500:500"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply the route-map while Importing vrf ISR's prefixes into "
+ "default vrf on router R1:"
+ )
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify on R1 that only prefixes with community value 100:100"
+ "in vrf ISR are imported to vrf default. While importing, the"
+ " community value has been stripped off:"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Applying route-map first followed by import VRF command.")
+ step(
+ "Apply the route-map while Importing vrf ISR's prefixes into "
+ "default vrf on router R1:"
+ )
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {"import": {"vrf": "ISR", "delete": True}}
+ }
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that until 'import VRF command' is not configured, "
+ "routes are not imported. After configuring 'import VRF command'"
+ " repeat step-4 for verification"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still present \n {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Delete/re-add import vrf ISR command multiple times in default" "vrf.")
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {"import": {"vrf": "ISR", "delete": True}}
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when import vrf ISR command is deleted, "
+ "all routes of vrf ISR disappear from default vrf and "
+ "when it's re-configured, repeat step-4 for verification."
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes are still present, Error {}".format(
+ tc_name, result
+ )
+
+ input_dict_isr["r1"]["bgp"][0]["address_family"][addr_type]["unicast"][
+ "import"
+ ]["delete"] = False
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Delete and re-configure route-map IMP from global config when "
+ "import and route-maps are applied in a ISR vrf."
+ )
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": "500:500"}},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Routes are still present, Error {}".format(
+ tc_name, result
+ )
+
+ input_dict_rm["r1"]["route_maps"]["rmap_IMP_{}".format(addr_type)][0][
+ "delete"
+ ] = False
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_comm = {"community": "500:500"}
+
+ result = verify_bgp_community(
+ tgen, addr_type, dut, [NETWORK1_1[addr_type]], input_dict_comm
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_cli_json_p1(request):
+ """
+ TC8_FUNC_9:
+ 1.5.9. Verifying the JSON outputs for all supported commands:
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ input_dict = {
+ "r1": {
+ "cli": [
+ "show bgp vrf default ipv4 summary",
+ "show bgp vrf all ipv6 summary",
+ "show bgp neighbors",
+ ]
+ }
+ }
+
+ result = verify_cli_json(tgen, input_dict)
+ 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/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
new file mode 100644
index 0000000..391c272
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak/test_bgp_vrf_dynamic_route_leak_topo2.py
@@ -0,0 +1,949 @@
+#!/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 BGP Multi-VRF Dynamic Route Leaking:
+
+1. Verify that Changing route-map configurations(match/set clauses) on
+ the fly it takes immediate effect.
+2. Verify BGP best path selection algorithm works fine when
+ routes are imported from ISR to default vrf and vice versa.
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+import platform
+
+# 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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ step,
+ create_route_maps,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ check_router_status,
+ get_frr_ipv6_linklocal,
+ shutdown_bringup_interface,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_attributes,
+ verify_best_path_as_per_bgp_attribute,
+ verify_bgp_rib,
+)
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/bgp_vrf_dynamic_route_leak_topo2.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": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"}
+NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"}
+
+PREFERRED_NEXT_HOP = "global"
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_bgp_best_path_with_dynamic_import_p0(request):
+ """
+ TC6_FUNC_6:
+ 1.5.6. Verify BGP best path selection algorithm works fine when
+ routes are imported from ISR to default vrf and vice versa.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Redistribute configured static routes into BGP process" " on R1/R2 and R3"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1", "r2", "r3", "r4"]
+ VRFS = ["ISR", "ISR", "default", "default"]
+ AS_NUM = [100, 100, 300, 400]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Import from default vrf into vrf ISR on R1 and R2 as below")
+
+ input_dict_vrf = {}
+ DUT = ["r1", "r2"]
+ VRFS = ["ISR", "ISR"]
+ AS_NUM = [100, 100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_vrf.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "default"}}}
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_default = {}
+ DUT = ["r1", "r2"]
+ VRFS = ["default", "default"]
+ AS_NUM = [100, 100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_default.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_default)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify ECMP/Next-hop/Imported routes Vs Locally originated "
+ "routes/eBGP routes vs iBGP routes --already covered in almost"
+ " all tests"
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify Pre-emption")
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r4_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Shutdown interface connected to r1 from r4:")
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False)
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ step("Verify next-hop is changed")
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bringup interface connected to r1 from r4:")
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True)
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ step("Verify next-hop is not chnaged aftr shutdown:")
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Active-Standby scenario(as-path prepend and Local pref)")
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create prefix-list")
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_ls_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK3_4[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set localpref 500")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 500},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-map to match prefix-list and set localpref 600")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 600},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_rma = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_PATH1_{}".format(
+ addr_type
+ ),
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_PATH2_{}".format(
+ addr_type
+ ),
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rma)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ attribute = "locPrf"
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is installed as per highest localpref")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set localpref 700")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 700},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is changed as per highest localpref")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set as-path prepend")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "localpref": 700,
+ "path": {"as_num": "111", "as_action": "prepend"},
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ attribute = "path"
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is changed as per shortest as-path")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_modify_route_map_match_set_clauses_p1(request):
+ """
+ TC13_CHAOS_4:
+ 1.5.13. Verify that Changing route-map configurations(match/set clauses) on
+ the fly it takes immediate effect.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure route-map to set community attribute for a specific"
+ "prefix on R1 in vrf ISR"
+ )
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_ABC_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_cl = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": "100:100",
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_XYZ_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pflist_ABC_{}".format(addr_type)
+ }
+ },
+ "set": {"community": {"num": "100:100"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply this route-map on R1 to vrf ISR while redistributing the"
+ " prefixes into BGP"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1"]
+ VRFS = ["ISR"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Configure another route-map for filtering the prefixes based on"
+ " community attribute while importing into default vrf"
+ )
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": "none"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Apply the route-map while Importing vrf ISR's prefixes into "
+ "default vrf on router R1:"
+ )
+
+ input_dict_isr = {}
+ DUT = ["r1"]
+ VRFS = ["default"]
+ AS_NUM = [100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_isr.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "route-map rmap_IMP_{}".format(addr_type)
+ }
+ }
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_isr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify on R1 that only prefixes with community value 100:100"
+ "in vrf ISR are imported to vrf default. While importing, the"
+ " community value has been stripped off:"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Add set clause in route-map IMP:")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {
+ "large_community": {"num": "100:100:100"},
+ "locPrf": 500,
+ "path": {"as_num": "100 100", "as_action": "prepend"},
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Verify that as we continue adding different attributes "
+ "step-by-step in route-map IMP those attributes gets "
+ "attached to prefixes:"
+ )
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ input_dict_comm = {"largeCommunity": "100:100:100"}
+
+ result = verify_bgp_community(
+ tgen, addr_type, dut, [NETWORK1_1[addr_type]], input_dict_comm
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_rmap = {
+ "r1": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [{"set": {"locPrf": 500}}]
+ }
+ }
+ }
+
+ result = verify_bgp_attributes(
+ tgen,
+ addr_type,
+ "r1",
+ [NETWORK1_1[addr_type]],
+ rmap_name="rmap_IMP_{}".format(addr_type),
+ input_dict=input_rmap,
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Change community-list to match a different value then " "100:100.")
+
+ input_dict_cl = {
+ "r1": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": "100:100",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "default"}
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r1", input_routes_r1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error : Routes are still " "present {}".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/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json
new file mode 100644
index 0000000..9c73baf
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/bgp_vrf_dynamic_route_leak_topo3.json
@@ -0,0 +1,1088 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r3-link1": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "RED"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r1-link3": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "GREEN"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r4-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r5-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r5": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py
new file mode 100644
index 0000000..a9aefc5
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo3/test_bgp_vrf_dynamic_route_leak_topo3.py
@@ -0,0 +1,1803 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify that with multiple tenant VRFs, dynamically imported routes are
+ further advertised to eBGP peers.
+2. Verify the route-map operations along with dynamic import command
+3. Verify that deleting static routes from originating VRF also deletes
+ routes from other VRFs and peers.
+4. Verify that deleting and adding "import" command multiple times shows
+ consistent results.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+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,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo3.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_routes_advertised_to_ebgp_peers_p0(request):
+ """
+ Verify that with multiple tenant VRFs, dynamically imported routes are
+ further advertised to eBGP peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R2 and R3 and redistribute in BGP for "
+ "BLUE and RED vrf instances"
+ )
+ for dut, network in zip(
+ ["r2", "r3"], [[NETWORK1_1, NETWORK1_2], [NETWORK2_1, NETWORK2_2]]
+ ):
+ for vrf_name, network_vrf in zip(["RED", "BLUE"], network):
+ step("Configure static route for VRF : {} on {}".format(vrf_name, dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [network_vrf[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut, as_num in zip(["r2", "r3"], ["2", "3"]):
+ for vrf_name in ["RED", "BLUE"]:
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ )
+
+ redist_dict = {
+ dut: {
+ "bgp": [
+ {"vrf": vrf_name, "local_as": as_num, "address_family": temp}
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R2 and R3 has installed redistributed routes in BLUE "
+ "and RED vrfs"
+ )
+ for dut, network in zip(
+ ["r2", "r3"], [[NETWORK2_1, NETWORK2_2], [NETWORK1_1, NETWORK1_2]]
+ ):
+ for vrf_name, network_vrf in zip(["RED", "BLUE"], network):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [network_vrf[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Import BLUE vrf's route in tenant vrf RED on R2 and then import "
+ "vrf RED's routes into BLUE vrf on R3"
+ )
+
+ for dut, as_num, vrf_name, vrf_import in zip(
+ ["r2", "r3"], ["2", "3"], ["RED", "BLUE"], ["BLUE", "RED"]
+ ):
+ step("Import vrf {} int vrf {}, on router {}".format(vrf_import, vrf_name, dut))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_import}}}})
+
+ import_dict = {
+ dut: {
+ "bgp": [{"vrf": vrf_name, "local_as": as_num, "address_family": temp}]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R2's vrf RED and R3's vrf BLUE has installed 4 set of "
+ "prefixes. Routes imported from BLUE vrf (originated R2's & received "
+ "from R3). Vrf RED's local routes (originated by R2's & received "
+ "from R3)"
+ )
+ step(
+ "Verify that R2 and R3 has installed redistributed routes in BLUE "
+ "and RED vrfs"
+ )
+
+ for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Additionally, R2 receives R3's BLUE vrf's prefixes and then import "
+ "into vrf RED. These imported routes are advertised back to "
+ "(originator)R3 but now in vrf RED, however R3 doesn't install these "
+ "in vrf RED. Denied due to own AS"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf BLUE from vrf RED's instance on R2.")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": "BLUE", "delete": True}}}}
+ )
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify on R3 that, there is no change in FIB of vrf BLUE and R2's "
+ "BLUE vrf originated routes are removed from vrf RED on R3."
+ )
+ for vrf_name in ["RED", "BLUE"]:
+ for addr_type in ADDR_TYPES:
+ if vrf_name == "RED":
+ network_vrf = [NETWORK1_1[addr_type], NETWORK2_1[addr_type]]
+ elif vrf_name == "BLUE":
+ network_vrf = [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": network_vrf,
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf BLUE from vrf RED's instance on R2.")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "BLUE"}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "All the routes described in earlier step should be added, once "
+ "import command on R2 is re-added."
+ )
+ for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf RED from BLUE vrf on R3")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": "RED", "delete": True}}}}
+ )
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "BLUE", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R2 that, there is no change in FIB of vrf RED and R3's "
+ "vrf RED's originated routes are removed from vrf BLUE on R2."
+ )
+ for vrf_name in ["RED", "BLUE"]:
+ for addr_type in ADDR_TYPES:
+ if vrf_name == "BLUE":
+ network_vrf = [NETWORK1_2[addr_type], NETWORK2_2[addr_type]]
+ elif vrf_name == "RED":
+ network_vrf = [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ]
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": network_vrf,
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add import vrf RED from BLUE vrf on R3")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "BLUE", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "All the routes described in earlier step should be added, once "
+ "import command on R2 is re-added."
+ )
+ for dut, vrf_name in zip(["r2", "r3"], ["RED", "BLUE"]):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK1_2[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_imported_matching_prefix_based_on_community_list_p0(request):
+ """
+ Verify the route-map operations along with dynamic import command
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for vrf RED and redistribute in BGP " "instance"
+ )
+ for vrf_name, networks in zip(
+ ["RED", "BLUE"], [[NETWORK1_1, NETWORK1_2], [NETWORK2_1, NETWORK2_2]]
+ ):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [networks[0][addr_type], networks[1][addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure route-map to set community attribute for a specific " "prefix on R3"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_pf = {
+ "r3": {
+ "prefix_lists": {
+ addr_type: {
+ "pflist_ABC_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK1_1[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_cl = {
+ "r3": {
+ "bgp_community_lists": [
+ {
+ "community_type": "expanded",
+ "action": "permit",
+ "name": "COMM",
+ "value": COMM_VAL_1,
+ }
+ ]
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_cl)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r3": {
+ "route_maps": {
+ "rmap_XYZ_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "match": {
+ addr_type: {
+ "prefix_lists": "pflist_ABC_{}".format(addr_type)
+ }
+ },
+ "set": {"community": {"num": COMM_VAL_1}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Apply this route-map on R3 to set community under vrf RED/BLUE "
+ "while redistributing the prefixes into BGP"
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static",
+ "attribute": {
+ "route-map": "rmap_XYZ_{}".format(addr_type)
+ },
+ }
+ ]
+ }
+ }
+ }
+ )
+
+ for vrf_name in ["RED", "BLUE"]:
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that specific prefixes matched in route-map have community "
+ "attribute value 100:100 tagged"
+ )
+ input_dict_comm = {"community": COMM_VAL_1}
+ for addr_type in ADDR_TYPES:
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [NETWORK1_1[addr_type]], input_dict_comm, vrf="RED"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Configure a route-map for filtering the prefixes based on community "
+ "attribute while importing into default vrf"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r3": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": COMM_VAL_2}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Apply the route-map while Importing vrf RED/BLUE's prefixes into "
+ "GREEN vrf on router R3"
+ )
+ temp = {}
+ for vrf_name in ["RED", "BLUE"]:
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ inport_dict = {
+ "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, inport_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "import": {"vrf": "route-map rmap_IMP_{}".format(addr_type)}
+ }
+ }
+ }
+ )
+
+ inport_dict = {
+ "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, inport_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_comm = {"community": COMM_VAL_2}
+ step(
+ "Verify on R3 that only prefixes with community value {} in vrf RED "
+ "are imported to vrf GREEN. While importing, the community value "
+ "has been changed to {}".format(COMM_VAL_1, COMM_VAL_2)
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [{"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"}]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK2_2[addr_type],
+ NETWORK1_2[addr_type],
+ ],
+ "vrf": "GREEN",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_bgp_community(
+ tgen, addr_type, "r3", [NETWORK1_1[addr_type]], input_dict_comm, vrf="GREEN"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Delete", "Add"], [True, False]):
+ step("{} import vrf RED/BLUE command one by one from vrf GREEN".format(action))
+ temp = {}
+ for vrf_name in ["RED", "BLUE"]:
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"import": {"vrf": vrf_name, "delete": value}}
+ }
+ }
+ )
+
+ inport_dict = {
+ "r3": {"bgp": [{"vrf": "GREEN", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, inport_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when import vrf RED/BLUE is {} one by one, all "
+ "routes of respective vrf disappear from vrf GREEN without "
+ "affecting (BLUE/RED) routes".format(action.lower())
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"}
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r3", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes["r3"]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r3", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes["r3"]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Delete", "Re-add"], [True, False]):
+ step(
+ "{} route-map IMP from global config when import and route-maps "
+ "are applied in vrf GREEN".format(action)
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_rm = {
+ "r3": {
+ "route_maps": {
+ "rmap_IMP_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {"community_list": {"id": "COMM"}},
+ "set": {"community": {"num": COMM_VAL_2}},
+ "delete": value,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when import vrf RED/BLUE is {} one by one, all "
+ "routes of respective vrf disappear from vrf GREEN without "
+ "affecting (BLUE/RED) routes".format(action.lower())
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": "GREEN"}
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r3", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes["r3"]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r3", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes["r3"]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_import_routes_delete_static_route_p1(request):
+ """
+ Verify that deleting static routes from originating VRF also deletes
+ routes from other VRFs and peers.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for each tenant vrf and redistribute "
+ "in respective BGP instance"
+ )
+ vrf_list = VRF_LIST + ["default"]
+ for vrf_name, network in zip(
+ vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1, NETWORK1_2]
+ ):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import routes among vrfs as mentioned below on router R3")
+
+ for vrf_name, vrf_import in zip(
+ ["GREEN", "BLUE", "default"], ["RED", "GREEN", "BLUE"]
+ ):
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_import}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, vrf_import, installed, not_installed in zip(
+ ["BLUE", "default"],
+ ["GREEN", "BLUE"],
+ [NETWORK3_1, NETWORK2_1],
+ [NETWORK1_1, NETWORK3_1],
+ ):
+ step(
+ "Verify that only locally originated routes of vrf {} are "
+ "advertised to vrf {}".format(vrf_import, vrf_name)
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [installed[addr_type]], "vrf": vrf_name}
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that non local originated routes {} of vrf {} are "
+ "not advertised to vrf {}".format(
+ not_installed[addr_type], vrf_import, vrf_name
+ )
+ )
+
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [not_installed[addr_type]], "vrf": vrf_name}
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r2"]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes["r2"]["static_routes"][0]["network"]
+ )
+
+ step("Delete static routes from vrf RED")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "Verify on R2 and R3, that only vrf RED and GREEN's RIB/FIB withdraw "
+ "deleted routes"
+ )
+ for dut in ["r2", "r3"]:
+ step(
+ "Verify on {}, that only vrf RED and GREEN's RIB/FIB withdraw "
+ "deleted routes".format(dut)
+ )
+ for vrf_name in ["RED", "GREEN"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK1_1[addr_type]], "vrf": vrf_name}
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, "r2", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes["r2"]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r2", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ step("Delete static routes from vrf BLUE")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "BLUE",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r2", "r3"]:
+ step(
+ "Verify on {}, that only default and BLUE vrf's RIB/FIB "
+ "withdraw deleted routes".format(dut)
+ )
+ for vrf_name in ["BLUE", "default"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": vrf_name}
+ ]
+ }
+ }
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ step("Delete static routes from vrf default")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_2[addr_type]],
+ "next_hop": "blackhole",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r2", "r3"]:
+ step(
+ "Verify on {}, that only default vrf RIB/FIB withdraw deleted "
+ "routes".format(dut)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK1_2[addr_type]], "vrf": vrf_name}
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add back all the routes that were deleted")
+ for vrf_name, network in zip(
+ vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1, NETWORK1_2]
+ ):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(vrf_list, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_import_routes_add_delete_import_command_p1(request):
+ """
+ Verify that deleting and adding "import" command multiple times shows
+ consistent results.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R2 for vrf RED and redistribute in "
+ "respective BGP instance"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF RED")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that R2 has installed redistributed routes in respective " "vrfs only")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import vrf RED's routes into vrf GREEN on R2")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R2, that it installs imported routes from vrf RED to vrf "
+ "GREEN's RIB/FIB"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("On R3 import routes from vrfs GREEN to default")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now imported into vrf default "
+ "of R3, next-hop pointing to vrf GREEN"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+
+ next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][addr_type].split("/")[0]
+ result = verify_bgp_rib(
+ tgen, addr_type, "r3", static_routes, next_hop=next_hop_1
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, next_hop=next_hop_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Delete import command from R3's default vrf instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)"
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": "GREEN", "delete": True}}}}
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now removed from vrf "
+ "default on R3, however vrf GREEN still retains those"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Delete import command from R2's vrf GREEN instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)"
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": "RED", "delete": True}}}}
+ )
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that R2's vrf RED routes are now removed from vrf GREEN "
+ "on R2 & R3 as well"
+ )
+ for dut in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step(
+ "Add import command from R3's default vrf instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)"
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that there are no routes installed on R3's vrf default " "RIB/FIB.")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Add import command from R2's vrf GREEN instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)."
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now imported into vrf "
+ "default of R3, next-hop pointing to vrf GREEN"
+ )
+ for dut in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}
+ ]
+ }
+ }
+ if dut == "r3":
+ next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][
+ addr_type
+ ].split("/")[0]
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_1
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ if dut == "r3":
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_1
+ )
+ else:
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Delete import command from R3's default vrf instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)."
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": "GREEN", "delete": True}}}}
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now removed from vrf "
+ "default on R3, however vrf GREEN still retains those."
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ static_routes = {
+ "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Delete redistribute static from R2 for vrf RED")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now removed from vrf GREEN "
+ "on R2 & R3 as well."
+ )
+ for dut in ["r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step(
+ "Add import command from R3's default vrf instance for both "
+ "address-families 1 by 1 (ipv4/ipv6)."
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that there are no routes installed on R3's vrf default " "RIB/FIB")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name, result, static_routes["r3"]["static_routes"][0]["network"]
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add redistribute static from R2 for vrf RED")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that R2's vrf RED routes are now imported into vrf "
+ "default of R3, next-hop pointing to vrf GREEN"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ next_hop_1 = topo["routers"]["r2"]["links"]["r3-link3"][addr_type].split("/")[0]
+ result = verify_bgp_rib(
+ tgen, addr_type, "r3", static_routes, next_hop=next_hop_1
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes, next_hop=next_hop_1)
+ 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/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json
new file mode 100644
index 0000000..9c73baf
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/bgp_vrf_dynamic_route_leak_topo4.json
@@ -0,0 +1,1088 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r3-link1": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "13.1.1.1/24", "ipv6": "13::1:1/120", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "RED"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r1-link3": {"ipv4": "13.1.1.2/24", "ipv6": "13::1:2/120", "vrf": "GREEN"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r4-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r5-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r5": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py
new file mode 100644
index 0000000..97016ca
--- /dev/null
+++ b/tests/topotests/bgp_vrf_dynamic_route_leak_topo4/test_bgp_vrf_dynamic_route_leak_topo4.py
@@ -0,0 +1,1909 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP Multi-VRF Dynamic Route Leaking:
+1. Verify recursive import among Tenant VRFs.
+2. Verify that dynamic import works fine between two different Tenant VRFs.
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+3. Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+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,
+ create_static_routes,
+ create_prefix_lists,
+ create_bgp_community_lists,
+ get_frr_ipv6_linklocal,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_dynamic_route_leak_topo4.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_recursive_import_tenant_vrf_p1(request):
+ """
+ Verify recursive import among Tenant VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R2 for vrf RED and redistribute in "
+ "respective BGP instance"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF RED")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that R2 has installed redistributed routes in vrf RED only")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "RED"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import vrf RED's routes into vrf GREEN on R2")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "RED"}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "GREEN", "local_as": 2, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R2, that it installs imported routes from vrf RED to vrf "
+ "GREEN's RIB/FIB pointing next-hop to vrf RED"
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "GREEN"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r2", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("On R3 import routes from vrf GREEN to vrf default")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "GREEN"}}}})
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R3, that it installs imported routes from vrf GREEN to "
+ "vrf default RIB/FIB pointing next-hop to vrf GREEN. "
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {"static_routes": [{"network": [NETWORK2_1[addr_type]]}]}
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("On R4 import routes from vrf default to vrf BLUE")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": "default"}}}})
+
+ import_dict = {
+ "r4": {"bgp": [{"vrf": "BLUE", "local_as": 4, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify on R4, that it installs imported routes from vrf default to "
+ "vrf BLUE RIB/FIB pointing next-hop to vrf default."
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r4": {
+ "static_routes": [{"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for dut, vrf_name, vrf_import, as_num in zip(
+ ["r2", "r4"], ["GREEN", "BLUE"], ["RED", "default"], [2, 4]
+ ):
+
+ for action, value in zip(["Delete", "Re-add"], [True, False]):
+ step("{} the import command on {} router".format(action, dut))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"import": {"vrf": vrf_import, "delete": value}}
+ }
+ }
+ )
+
+ import_dict = {
+ dut: {
+ "bgp": [
+ {"vrf": vrf_name, "local_as": as_num, "address_family": temp}
+ ]
+ }
+ }
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r4": {
+ "static_routes": [
+ {"network": [NETWORK2_1[addr_type]], "vrf": "BLUE"}
+ ]
+ }
+ }
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, "r4", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes["r4"]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, "r4", static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes["r4"]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r4", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_import_routes_between_two_tenant_vrf_p0(request):
+ """
+ Verify that dynamic import works fine between two different Tenant VRFs.
+
+ When next-hop IPs are same across all VRFs.
+ When next-hop IPs are different across all VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import from vrf GREEN+BLUE into vrf RED on R3")
+
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R1, that it installs all the routes(local+imported) in "
+ "vrf RED's RIB/FIB and doesn't get confuse with next-hop attribute, "
+ "as all vrfs on R1 are using same IP address for next-hop"
+ )
+
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ next_hop_1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[0]
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", static_routes, next_hop=next_hop_1
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r1", static_routes, next_hop=next_hop_1)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf GREEN/BLUE/Both command from vrf RED's instance on" " R3")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R3")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R1-R3 and R1-R2 for vrf GREEN, BLUE "
+ "and default".format(action)
+ )
+ bgp_disable = {"r3": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "default"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r3-link1": {"shutdown": value}}
+ },
+ "r2": {
+ "dest_link": {"r3-link1": {"shutdown": value}}
+ },
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r3"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 3, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R1-R3 and R1-R2 for vrf RED".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3-link1": {"shutdown": value}}},
+ "r2": {"dest_link": {"r3-link1": {"shutdown": value}}},
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import command from router R3 and configure the same on R2")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that once import commands are removed from R3, all imported "
+ "routes are withdrawn from RIB/FIB of vrf RED on R1/R2/R3"
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step(
+ "Configure static routes on R2 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after import commands are re-configured on R2's vrf RED, all "
+ "those routes are installed again in vrf RED of R1,R2,R3"
+ )
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Remove/add import vrf GREEN/BLUE/both command from vrf RED's " "instance on R2"
+ )
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that R1,R2 & R3 withdraw imported routes from vrf RED's RIB")
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+
+ step("Add import vrf GREEN/BLUE/Both command from vrf RED's instance on " "R2")
+ for vrf_name in ["BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": "RED", "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [NETWORK2_1[addr_type], NETWORK3_1[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R2-R3 for vrf GREEN, BLUE and default".format(
+ action
+ )
+ )
+ bgp_disable = {"r2": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "default"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {"r2-link1": {"shutdown": value}}
+ }
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r2"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 2, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R2-R3 for vrf RED".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r2": {"dest_link": {"r3-link1": {"shutdown": value}}}
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {
+ "r3": {"bgp": [{"vrf": "RED", "local_as": 3, "address_family": temp}]}
+ }
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from RED vrf's RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ "vrf": "RED",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_dynamic_import_routes_between_tenant_to_default_vrf_p0(request):
+ """
+ Verify that with multiple tenant VRFs, dynamic import works fine between
+ Tenant VRFs to default VRF.
+
+ When next-hop IPs and prefixes are same across all VRFs.
+ When next-hop IPs and prefixes are different across all VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ reset_config_on_routers(tgen)
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step(
+ "Configure static routes on R3 for each vrf and redistribute in "
+ "respective BGP instance"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {}".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step(
+ "Verify that R3 has installed redistributed routes in respective "
+ "vrfs: {}".format(vrf_name)
+ )
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Import all tenant vrfs(GREEN+BLUE+RED) in default vrf on R3")
+
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ redist_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify on R3 that it installs all the routes(imported from tenant "
+ "VRFs) in default vrf. Additionally, verify that R1 & R2 also "
+ "receive these routes from R3 and install in default vrf, next-hop "
+ "pointing to R3"
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ for dut in ["r2", "r1"]:
+ next_hop_val = topo["routers"]["r3"]["links"]["{}-link4".format(dut)][
+ addr_type
+ ].split("/")[0]
+
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_val
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, next_hop=next_hop_val
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Remove", "Add"], [True, False], ["withdraw", "re-install"]
+ ):
+ step(
+ "{} import vrf GREEN/BLUE/RED/all command from default vrf "
+ "instance on R3".format(action)
+ )
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {"import": {"vrf": vrf_name, "delete": value}}
+ }
+ }
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1,R2 & R3 {} imported routes from GREEN/BLUE/RED/all"
+ " in default vrf's RIB".format(status)
+ )
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R1-R3 and R1-R2 for vrf RED, GREEN "
+ "and BLUE".format(action)
+ )
+ bgp_disable = {"r3": {"bgp": []}}
+ for vrf_name in ["RED", "GREEN", "BLUE"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {"r3-link4": {"shutdown": value}}
+ },
+ "r2": {
+ "dest_link": {"r3-link4": {"shutdown": value}}
+ },
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r3"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 3, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that when peering is shutdown for tenant vrfs, it "
+ "doesn't impact the RIB/FIB of default vrf on router R1 and R2"
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step(
+ "{} the neighborship between R1-R3 and R2-R3 for default vrf".format(action)
+ )
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {"dest_link": {"r3-link4": {"shutdown": value}}},
+ "r2": {"dest_link": {"r3-link4": {"shutdown": value}}},
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from default vrf's RIB"
+ " and FIB".format(status)
+ )
+ for dut in ["r1", "r2"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import command from router R3 and configure the same on R2")
+ temp = {}
+ for vrf_name in VRF_LIST:
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {"r3": {"bgp": [{"local_as": 3, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that once import commands are removed from R3, all imported "
+ "routes are withdrawn from RIB/FIB of default vrf on R1/R2/R3"
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for vrf_name, network in zip(VRF_LIST, [NETWORK1_1, NETWORK2_1, NETWORK3_1]):
+ step("Configure static route for VRF : {} on r2".format(vrf_name))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": [network[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"redistribute": [{"redist_type": "static"}]}}}
+ )
+
+ redist_dict = {
+ "r2": {"bgp": [{"vrf": vrf_name, "local_as": 2, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove redistribute static route on BGP VRF : {} on r3".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "redistribute": [{"redist_type": "static", "delete": True}]
+ }
+ }
+ }
+ )
+
+ redist_dict = {
+ "r3": {"bgp": [{"vrf": vrf_name, "local_as": 3, "address_family": temp}]}
+ }
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after import commands are re-configured on R2's vrf RED, all "
+ "those routes are installed again in default vrf of R1,R2,R3"
+ )
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Remove import vrf RED/GREEN/BLUE/all one by one from default vrf" " on R2")
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {addr_type: {"unicast": {"import": {"vrf": vrf_name, "delete": True}}}}
+ )
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1, R2 and R3 withdraws imported routes from default "
+ "vrf's RIB and FIB "
+ )
+ for dut in ["r1", "r2", "r3"]:
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError {}\n"
+ "Routes {} still in BGP table".format(
+ tc_name, result, static_routes[dut]["static_routes"][0]["network"]
+ )
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Add import vrf RED/GREEN/BLUE/all one by one from default vrf on R2")
+ for vrf_name in ["RED", "BLUE", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({addr_type: {"unicast": {"import": {"vrf": vrf_name}}}})
+
+ import_dict = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify that {} reinstall imported routes from vrf RED's RIB".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value in zip(["Shut", "No shut"], [True, False]):
+ step(
+ "{} the neighborship between R2-R3 for vrf GREEN, BLUE and RED".format(
+ action
+ )
+ )
+ bgp_disable = {"r2": {"bgp": []}}
+ for vrf_name in ["GREEN", "BLUE", "RED"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {"r2-link4": {"shutdown": value}}
+ }
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable["r2"]["bgp"].append(
+ {"vrf": vrf_name, "local_as": 2, "address_family": temp}
+ )
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify RIB/FIB of vrf RED will be unchanged on all 3 routers")
+ for dut in ["r1", "r2", "r3"]:
+ step("Verify RIB/FIB for vrf RED on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for action, value, status in zip(
+ ["Shut", "No shut"], [True, False], ["Withdraw", "Reinstall"]
+ ):
+ step("{} the neighborship between R2-R3 for default vrf".format(action))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update(
+ {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {"dest_link": {"r2-link4": {"shutdown": value}}}
+ }
+ }
+ }
+ }
+ )
+
+ bgp_disable = {"r2": {"bgp": [{"local_as": 2, "address_family": temp}]}}
+ result = create_router_bgp(tgen, topo, bgp_disable)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify that R1 and R2 {} all the routes from default vrfs RIB and"
+ " FIB".format(status)
+ )
+ for dut in ["r1", "r3"]:
+ step("Verify RIB/FIB for default vrf on {}".format(dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [
+ NETWORK1_1[addr_type],
+ NETWORK2_1[addr_type],
+ NETWORK3_1[addr_type],
+ ],
+ "next_hop": "blackhole",
+ }
+ ]
+ }
+ }
+
+ if value:
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError {}\n" "Routes {} still in BGP table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, static_routes, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error {}" "Routes {} still in Route table".format(
+ tc_name,
+ result,
+ static_routes[dut]["static_routes"][0]["network"],
+ )
+ else:
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, addr_type, dut, static_routes)
+ 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/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf
new file mode 100644
index 0000000..66493f0
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/bgpd.conf
@@ -0,0 +1,23 @@
+frr defaults traditional
+!
+hostname ce1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65002
+ bgp router-id 192.0.2.2
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor eth0 interface
+ neighbor eth0 remote-as external
+ neighbor eth0 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor eth0 activate
+ redistribute connected
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf
new file mode 100644
index 0000000..a163295
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/ce1/zebra.conf
@@ -0,0 +1,13 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface lo
+ ip address 172.16.0.1/32
+!
+interface eth0
+ ipv6 nd ra-interval 1
+ no ipv6 nd suppress-ra
+!
+line vty
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf
new file mode 100644
index 0000000..c5c9927
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/bgpd.conf
@@ -0,0 +1,41 @@
+frr defaults traditional
+!
+hostname pe1
+password zebra
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+router bgp 65001
+ bgp router-id 192.0.2.1
+ !
+!
+router bgp 65001 vrf vrf10
+ bgp router-id 192.0.2.1
+ no bgp ebgp-requires-policy
+ no bgp default ipv4-unicast
+ neighbor eth0 interface
+ neighbor eth0 remote-as external
+ neighbor eth0 timers connect 1
+ !
+ address-family ipv4 unicast
+ neighbor eth0 activate
+ rd vpn export 65001:10
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
+router bgp 65001 vrf vrf20
+ bgp router-id 192.0.2.1
+ !
+ address-family ipv4 unicast
+ rd vpn export 65001:20
+ rt vpn both 0:10
+ import vpn
+ export vpn
+ exit-address-family
+ !
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json
new file mode 100644
index 0000000..9516016
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/default_ipv4_vpn.json
@@ -0,0 +1,32 @@
+{
+ "vrfName": "default",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "routeDistinguishers": {
+ "65001:10": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "used": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json
new file mode 100644
index 0000000..768bffb
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf10_ipv4_unicast.json
@@ -0,0 +1,32 @@
+{
+ "vrfName": "vrf10",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "nexthops": [
+ {
+ "hostname": "ce1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ },
+ {
+ "hostname": "ce1",
+ "afi": "ipv6",
+ "scope": "link-local"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json
new file mode 100644
index 0000000..1e93715
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/results/vrf20_ipv4_unicast.json
@@ -0,0 +1,34 @@
+{
+ "vrfName": "vrf20",
+ "routerId": "192.0.2.1",
+ "localAS": 65001,
+ "routes": {
+ "172.16.0.1/32": [
+ {
+ "valid": true,
+ "bestpath": true,
+ "pathFrom": "external",
+ "prefix": "172.16.0.1",
+ "prefixLen": 32,
+ "network": "172.16.0.1\/32",
+ "path": "65002",
+ "origin": "incomplete",
+ "announceNexthopSelf": true,
+ "nhVrfName": "vrf10",
+ "nexthops": [
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "scope": "global",
+ "used": true
+ },
+ {
+ "hostname": "pe1",
+ "afi": "ipv6",
+ "scope": "link-local"
+ }
+ ]
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf
new file mode 100644
index 0000000..d40041a
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/pe1/zebra.conf
@@ -0,0 +1,10 @@
+log file zebra.log
+!
+hostname pe1
+!
+interface eth0 vrf vrf10
+ ipv6 nd ra-interval 1
+ no ipv6 nd suppress-ra
+!
+line vty
+!
diff --git a/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py
new file mode 100755
index 0000000..dd27ad3
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_5549_routes/test_bgp_vrf_leaking.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2022, LINE Corporation
+# Authored by Ryoga Saito <ryoga.saito@linecorp.com>
+#
+# 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.
+#
+
+import os
+import re
+import sys
+import json
+import functools
+import pytest
+
+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 required_linux_kernel_version
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("pe1")
+ tgen.add_router("ce1")
+
+ tgen.add_link(tgen.gears["pe1"], tgen.gears["ce1"], "eth0", "eth0")
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ for rname, router in tgen.routers().items():
+ router.load_config(TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, '{}/zebra.conf'.format(rname)))
+ router.load_config(TopoRouter.RD_BGP,
+ os.path.join(CWD, '{}/bgpd.conf'.format(rname)))
+
+ tgen.gears["pe1"].run("ip link add vrf10 type vrf table 10")
+ tgen.gears["pe1"].run("ip link set vrf10 up")
+ tgen.gears["pe1"].run("ip link add vrf20 type vrf table 20")
+ tgen.gears["pe1"].run("ip link set vrf20 up")
+ tgen.gears["pe1"].run("ip link set eth0 master vrf10")
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def open_json_file(path):
+ try:
+ with open(path, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(path)
+
+
+def check_vrf10_rib(output):
+ expected = open_json_file("%s/pe1/results/vrf10_ipv4_unicast.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check_default_vpn_rib(output):
+ expected = open_json_file("%s/pe1/results/default_ipv4_vpn.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check_vrf20_rib(output):
+ expected = open_json_file("%s/pe1/results/vrf20_ipv4_unicast.json" % CWD)
+ actual = json.loads(output)
+ return topotest.json_cmp(actual, expected)
+
+
+def check(name, command, checker):
+ tgen = get_topogen()
+ router = tgen.gears[name]
+
+ def _check():
+ try:
+ return checker(router.vtysh_cmd(command))
+ except:
+ return False
+
+ logger.info('[+] check {} "{}"'.format(name, command))
+ _, result = topotest.run_and_expect(_check, None, count=10, wait=0.5)
+ assert result is None, "Failed"
+
+
+def test_rib():
+ check("pe1", "show bgp vrf vrf10 ipv4 unicast json", check_vrf10_rib)
+ check("pe1", "show bgp ipv4 vpn json", check_default_vpn_rib)
+ check("pe1", "show bgp vrf vrf20 ipv4 unicast json", check_vrf20_rib)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/__init__.py
diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf
new file mode 100644
index 0000000..9e0a4a8
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/bgpd.conf
@@ -0,0 +1,31 @@
+!
+router bgp 65500
+exit
+!
+router bgp 65500 vrf vrf1
+ bgp router-id 10.0.0.1
+ no bgp network import-check
+ address-family ipv4 unicast
+ network 192.168.100.100/32 route-map rm
+ rd vpn export 65500:10001
+ rt vpn import 65500:10000 65500:10990
+ rt vpn export 65500:10000
+ export vpn
+ import vpn
+ exit-address-family
+exit
+!
+router bgp 65500 vrf vrf2
+ address-family ipv4 unicast
+ rd vpn export 65500:11001
+ rt vpn import 65500:11000 65500:11990
+ rt vpn export 65500:11000
+ export vpn
+ import vpn
+ exit-address-family
+exit
+!
+route-map rm permit 10
+ set extcommunity rt 65500:10100 65500:11990
+exit
+!
diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf
new file mode 100644
index 0000000..22a26ac
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py
new file mode 100644
index 0000000..3624340
--- /dev/null
+++ b/tests/topotests/bgp_vrf_leaking_rt_change_route_maps/test_bgp_vrf_leaking_rt_change_route_maps.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 by
+# Donatas Abraitis <donatas@opensourcerouting.org>
+#
+# 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.
+#
+
+"""
+If we overwrite import/export RT list via route-maps or even flush by using
+`set extcommunity none`, then we must withdraw old paths from VRFs to avoid
+stale paths.
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.common_config import step
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router = tgen.gears["r1"]
+ router.cmd_raises("ip link add vrf1 type vrf table 10")
+ router.cmd_raises("ip link set up dev vrf1")
+ router.cmd_raises("ip link add vrf2 type vrf table 20")
+ router.cmd_raises("ip link set up dev vrf2")
+ router.load_config(TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf"))
+ router.load_config(TopoRouter.RD_BGP, os.path.join(CWD, "r1/bgpd.conf"))
+ router.start()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_bgp_vrf_leaking_rt_change_route_maps():
+ tgen = get_topogen()
+
+ router = tgen.gears["r1"]
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_check_path():
+ output = json.loads(router.vtysh_cmd("show bgp vrf vrf2 ipv4 unicast json"))
+ expected = {"routes": {"192.168.100.100/32": [{"nhVrfName": "vrf1"}]}}
+ return topotest.json_cmp(output, expected)
+
+ step("Initial converge")
+ test_func = functools.partial(_bgp_check_path)
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, "Can't see 192.168.100.100/32 leaked from vrf1 into vrf2."
+
+ step("Overwrite RT list (remove rt 65500:11990 from route-map)")
+ router.vtysh_cmd(
+ """
+ config terminal
+ route-map rm permit 10
+ set extcommunity rt 65500:10100
+ exit
+ """
+ )
+
+ step("Check if 192.168.100.100/32 was removed from vrf2")
+ test_func = functools.partial(_bgp_check_path)
+ _, result = topotest.run_and_expect(test_func, not None, count=20, wait=0.5)
+ assert result is not None, "192.168.100.100/32 still exists in vrf2 as stale."
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json
new file mode 100644
index 0000000..b1d7d09
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo1.json
@@ -0,0 +1,563 @@
+{
+ "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": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["11.11.11.1/32", "11.11.11.11/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["11:11::1/128", "11:11::11/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["10.10.10.10/32", "10.10.10.100/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["10:10::10/128", "10:10::100/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "ISR"},
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {
+ "name": "ISR",
+ "id": "1"
+ }
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "next_hop_self": true,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["22.22.22.2/32", "22.22.22.22/32"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["22:22::2/128", "22:22::22/128"],
+ "next_hop":"Null0",
+ "vrf": "ISR"
+ },
+ {
+ "network": ["20.20.20.20/32", "20.20.20.200/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["20:20::20/128", "20:20::200/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["30.30.30.3/32", "30.30.30.30/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["30:30::3/128", "30:30::30/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r1-link1": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "bgp":
+ [
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "static_routes":[
+ {
+ "network": ["40.40.40.4/32", "40.40.40.40/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["40:40::4/128", "40:40::40/128"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50.50.50.5/32", "50.50.50.50/32"],
+ "next_hop":"Null0"
+ },
+ {
+ "network": ["50:50::5/128", "50:50::50/128"],
+ "next_hop":"Null0"
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json
new file mode 100644
index 0000000..0b13882
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/bgp_vrf_lite_best_path_topo2.json
@@ -0,0 +1,1088 @@
+{
+ "address_types": ["ipv4","ipv6"],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r3-link1": {"ipv4": "192.168.1.1/24", "ipv6": "fd00:0:0:1::1/120", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "192.168.1.1/24", "ipv6": "fd00:0:0:1::1/120", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "1",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "1",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r2": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "2",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r3": {
+ "links": {
+ "r1-link1": {"ipv4": "192.168.1.2/24", "ipv6": "fd00:0:0:1::2/120", "vrf": "RED"},
+ "r1-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r1-link3": {"ipv4": "192.168.1.2/24", "ipv6": "fd00:0:0:1::2/120", "vrf": "GREEN"},
+ "r1-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r4-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r4-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r4-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r4-link4": {"ipv4": "auto", "ipv6": "auto"},
+ "r5-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r5-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r5-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r5-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link2": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link3": {
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ },
+ "r5": {
+ "dest_link": {
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "route_maps": [{
+ "name": "rmap_global",
+ "direction": "in"
+ }]
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "route_maps": {
+ "rmap_global": [{
+ "action": "permit",
+ "set": {
+ "ipv6": {
+ "nexthop": "prefer-global"
+ }
+ }
+ }]
+ }
+ },
+ "r4": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "4",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "r5": {
+ "links": {
+ "r3-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED"},
+ "r3-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE"},
+ "r3-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "GREEN"},
+ "r3-link4": {"ipv4": "auto", "ipv6": "auto"}
+ },
+ "vrfs":[
+ {"name": "RED", "id": "1"},
+ {"name": "BLUE", "id": "2"},
+ {"name": "GREEN", "id": "3"}
+ ],
+ "bgp":
+ [
+ {
+ "local_as": "3",
+ "vrf": "RED",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "3",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r5-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py
new file mode 100644
index 0000000..b95e71c
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo1.py
@@ -0,0 +1,916 @@
+#!/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 BGP VRF Lite:
+
+1. Verify BGP best path selection algorithm works fine when
+routes are imported from ISR to default vrf and vice versa.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+
+# 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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ step,
+ create_route_maps,
+ create_prefix_lists,
+ check_router_status,
+ get_frr_ipv6_linklocal,
+ shutdown_bringup_interface,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_community,
+ verify_bgp_rib,
+ clear_bgp,
+ verify_best_path_as_per_bgp_attribute
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.10/32", "ipv6": "10:10::10/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+NETWORK3_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"}
+NETWORK3_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"}
+
+NETWORK4_1 = {"ipv4": "40.40.40.4/32", "ipv6": "40:40::4/128"}
+NETWORK4_2 = {"ipv4": "40.40.40.40/32", "ipv6": "40:40::40/128"}
+NETWORK4_3 = {"ipv4": "50.50.50.5/32", "ipv6": "50:50::5/128"}
+NETWORK4_4 = {"ipv4": "50.50.50.50/32", "ipv6": "50:50::50/128"}
+NEXT_HOP_IP = {"ipv4": "Null0", "ipv6": "Null0"}
+LOOPBACK_1 = {
+ "ipv4": "10.0.0.7/24",
+ "ipv6": "fd00:0:0:1::7/64",
+ "ipv4_mask": "255.255.255.0",
+ "ipv6_mask": None,
+}
+LOOPBACK_2 = {
+ "ipv4": "10.0.0.16/24",
+ "ipv6": "fd00:0:0:3::5/64",
+ "ipv4_mask": "255.255.255.0",
+ "ipv6_mask": None,
+}
+PREFERRED_NEXT_HOP = "global"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_lite_best_path_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 disable_route_map_to_prefer_global_next_hop(tgen, topo):
+ """
+ This API is to remove prefer global route-map applied on neighbors
+
+ Parameter:
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : Input JSON data
+
+ Returns:
+ --------
+ True/errormsg
+
+ """
+
+ logger.info("Remove prefer-global rmap applied on neighbors")
+ input_dict = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r2": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "vrf": "ISR",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r3": {
+ "bgp": [
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "300",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ "r4": {
+ "bgp": [
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ {
+ "local_as": "400",
+ "address_family": {
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_global",
+ "direction": "in",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ },
+ ]
+ },
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase :Failed \n Error: {}".format(result)
+
+ return True
+
+
+def test_bgp_best_path_with_dynamic_import_p0(request):
+ """
+ 1.5.6. Verify BGP best path selection algorithm works fine when
+ routes are imported from ISR to default vrf and vice versa.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ build_config_from_json(tgen, topo)
+
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ for addr_type in ADDR_TYPES:
+
+ step(
+ "Redistribute configured static routes into BGP process" " on R1/R2 and R3"
+ )
+
+ input_dict_1 = {}
+ DUT = ["r1", "r2", "r3", "r4"]
+ VRFS = ["ISR", "ISR", "default", "default"]
+ AS_NUM = [100, 100, 300, 400]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_1.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Import from default vrf into vrf ISR on R1 and R2 as below")
+
+ input_dict_vrf = {}
+ DUT = ["r1", "r2"]
+ VRFS = ["ISR", "ISR"]
+ AS_NUM = [100, 100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_vrf.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "default"}}}
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_vrf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_default = {}
+ DUT = ["r1", "r2"]
+ VRFS = ["default", "default"]
+ AS_NUM = [100, 100]
+
+ for dut, vrf, as_num in zip(DUT, VRFS, AS_NUM):
+ temp = {dut: {"bgp": []}}
+ input_dict_default.update(temp)
+
+ temp[dut]["bgp"].append(
+ {
+ "local_as": as_num,
+ "vrf": vrf,
+ "address_family": {
+ addr_type: {"unicast": {"import": {"vrf": "ISR"}}}
+ },
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, input_dict_default)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify ECMP/Next-hop/Imported routes Vs Locally originated "
+ "routes/eBGP routes vs iBGP routes --already covered in almost"
+ " all tests"
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify Pre-emption")
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r4_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Shutdown interface connected to r1 from r4:")
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, False)
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ step("Verify next-hop is changed")
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Bringup interface connected to r1 from r4:")
+ shutdown_bringup_interface(tgen, "r4", intf_r4_r1, True)
+
+ for addr_type in ADDR_TYPES:
+
+ input_routes_r3 = {
+ "r3": {"static_routes": [{"network": [NETWORK3_3[addr_type]]}]}
+ }
+
+ intf_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"]["interface"]
+ intf_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"]["interface"]
+
+ if addr_type == "ipv6" and "link_local" in PREFERRED_NEXT_HOP:
+ nh_r3_r1 = get_frr_ipv6_linklocal(tgen, "r3", intf=intf_r3_r1)
+ nh_r4_r1 = get_frr_ipv6_linklocal(tgen, "r4", intf=intf_r4_r1)
+ else:
+ nh_r3_r1 = topo["routers"]["r3"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+ nh_r4_r1 = topo["routers"]["r4"]["links"]["r1-link1"][addr_type].split("/")[
+ 0
+ ]
+
+ step("Verify next-hop is not chnaged aftr shutdown:")
+ result = verify_bgp_rib(
+ tgen, addr_type, "r1", input_routes_r3, next_hop=[nh_r3_r1]
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ step("Active-Standby scenario(as-path prepend and Local pref)")
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create prefix-list")
+
+ input_dict_pf = {
+ "r1": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_ls_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": NETWORK3_4[addr_type],
+ "action": "permit",
+ }
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_pf)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set localpref 500")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 500},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create route-map to match prefix-list and set localpref 600")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 600},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_rma = {
+ "r1": {
+ "bgp": [
+ {
+ "local_as": "100",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_PATH1_{}".format(
+ addr_type
+ ),
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r1-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_PATH2_{}".format(
+ addr_type
+ ),
+ "direction": "in",
+ }
+ ]
+ }
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_rma)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r1"
+ attribute = "locPrf"
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is installed as per highest localpref")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set localpref 700")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {"locPrf": 700},
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is changed as per highest localpref")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, attribute
+ )
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ for addr_type in ADDR_TYPES:
+
+ step("Create route-map to match prefix-list and set as-path prepend")
+
+ input_dict_rm = {
+ "r1": {
+ "route_maps": {
+ "rmap_PATH2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_ls_{}".format(addr_type)
+ }
+ },
+ "set": {
+ "localpref": 700,
+ "path": {"as_num": "111", "as_action": "prepend"},
+ },
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_route_maps(tgen, input_dict_rm)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ attribute = "path"
+
+ for addr_type in ADDR_TYPES:
+
+ step("Verify bestpath is changed as per shortest as-path")
+
+ input_routes_r3 = {
+ "r3": {
+ "static_routes": [
+ {"network": [NETWORK3_3[addr_type], NETWORK3_4[addr_type]]}
+ ]
+ }
+ }
+
+ result = verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, dut, input_routes_r3, 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/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py
new file mode 100644
index 0000000..9291fbd
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_best_path_test/test_bgp_vrf_lite_best_path_topo2.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2021 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 BGP VRF Lite:
+1. Verify that locally imported routes are selected as best path over eBGP imported routes
+ peers.
+2. Verify ECMP for imported routes from different VRFs.
+"""
+
+import os
+import sys
+import time
+import pytest
+import platform
+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.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ create_static_routes,
+ check_router_status,
+ apply_raw_config
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_bgp_rib,
+ verify_bgp_bestpath
+)
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+NETWORK1_1 = {"ipv4": "11.11.11.1/32", "ipv6": "11:11::1/128"}
+NETWORK1_2 = {"ipv4": "11.11.11.11/32", "ipv6": "11:11::11/128"}
+NETWORK1_3 = {"ipv4": "10.10.10.1/32", "ipv6": "10:10::1/128"}
+NETWORK1_4 = {"ipv4": "10.10.10.100/32", "ipv6": "10:10::100/128"}
+NETWORK1_5 = {"ipv4": "110.110.110.1/32", "ipv6": "110:110::1/128"}
+NETWORK1_6 = {"ipv4": "110.110.110.100/32", "ipv6": "110:110::100/128"}
+
+NETWORK2_1 = {"ipv4": "22.22.22.2/32", "ipv6": "22:22::2/128"}
+NETWORK2_2 = {"ipv4": "22.22.22.22/32", "ipv6": "22:22::22/128"}
+NETWORK2_3 = {"ipv4": "20.20.20.20/32", "ipv6": "20:20::20/128"}
+NETWORK2_4 = {"ipv4": "20.20.20.200/32", "ipv6": "20:20::200/128"}
+NETWORK2_5 = {"ipv4": "220.220.220.20/32", "ipv6": "220:220::20/128"}
+NETWORK2_6 = {"ipv4": "220.220.220.200/32", "ipv6": "220:220::200/128"}
+
+NETWORK3_1 = {"ipv4": "30.30.30.3/32", "ipv6": "30:30::3/128"}
+NETWORK3_2 = {"ipv4": "30.30.30.30/32", "ipv6": "30:30::30/128"}
+
+PREFIX_LIST = {
+ "ipv4": ["11.11.11.1", "22.22.22.2", "22.22.22.22"],
+ "ipv6": ["11:11::1", "22:22::2", "22:22::22"],
+}
+PREFERRED_NEXT_HOP = "global"
+VRF_LIST = ["RED", "BLUE", "GREEN"]
+COMM_VAL_1 = "100:100"
+COMM_VAL_2 = "500:500"
+COMM_VAL_3 = "600:600"
+BESTPATH = {
+ "ipv4": "0.0.0.0",
+ "ipv6": "::"
+}
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/bgp_vrf_lite_best_path_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # Run these tests for kernel version 4.19 or above
+ if version_cmp(platform.release(), "4.19") < 0:
+ error_msg = (
+ "BGP vrf dynamic route leak tests will not run "
+ '(have kernel "{}", but it requires >= 4.19)'.format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ 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("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 test_dynamic_import_ecmp_imported_routed_diffrent_vrfs_p0(request):
+ """
+ Verify ECMP for imported routes from different VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step("Configure same static routes in tenant vrfs RED and GREEN on router "
+ "R3 and redistribute in respective BGP process")
+
+ for vrf_name in ["RED", "GREEN"]:
+ for addr_type in ADDR_TYPES:
+ if vrf_name == "GREEN":
+ next_hop_vrf = topo["routers"]["r1"]["links"][
+ "r3-link3"][addr_type].split("/")[0]
+ else:
+ next_hop_vrf = topo["routers"]["r2"]["links"][
+ "r3-link1"][addr_type].split("/")[0]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": next_hop_vrf,
+ "vrf": vrf_name
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({
+ addr_type: {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ })
+
+ redist_dict = {"r3": {"bgp": [{
+ "vrf": vrf_name, "local_as": 3, "address_family": temp
+ }]}}
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that configured static routes are installed in respective "
+ "BGP table for vrf RED & GREEN")
+ for vrf_name in ["RED", "GREEN"]:
+ for addr_type in ADDR_TYPES:
+ if vrf_name == "GREEN":
+ next_hop_vrf = topo["routers"]["r1"]["links"][
+ "r3-link3"][addr_type].split("/")[0]
+ else:
+ next_hop_vrf = topo["routers"]["r2"]["links"][
+ "r3-link1"][addr_type].split("/")[0]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "vrf": vrf_name
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("Import vrf RED and GREEN into default vrf and Configure ECMP")
+ bgp_val = []
+ for vrf_name in ["RED", "GREEN"]:
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": vrf_name
+ },
+ "maximum_paths": {
+ "ebgp": 2
+ }
+ }
+ }
+ })
+
+ bgp_val.append({
+ "local_as": 3, "address_family": temp
+ })
+
+ import_dict = {"r3": {"bgp": bgp_val}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Configure bgp bestpath on router r3")
+ r3_raw_config = {
+ "r3": {
+ "raw_config": [
+ "router bgp 3",
+ "bgp bestpath as-path multipath-relax"
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, r3_raw_config)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that routes are imported with two different next-hop vrfs "
+ "and IPs. Additionally R3 must do ECMP for both the routes.")
+
+ for addr_type in ADDR_TYPES:
+ next_hop_vrf = [
+ topo["routers"]["r2"]["links"]["r3-link1"][addr_type]. \
+ split("/")[0],
+ topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \
+ split("/")[0]
+ ]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("Now change the next-hop of static routes in vrf RED and GREEN to "
+ "same IP address")
+ for addr_type in ADDR_TYPES:
+ next_hop_vrf = topo["routers"]["r1"]["links"][
+ "r3-link3"][addr_type].split("/")[0]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": next_hop_vrf,
+ "vrf": "RED"
+ },
+ {
+ "network": [NETWORK1_1[addr_type]],
+ "next_hop": topo["routers"]["r2"]["links"][
+ "r3-link1"][addr_type].split("/")[0],
+ "vrf": "RED",
+ "delete": True
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that now routes are imported with two different next-hop "
+ "vrfs but same IPs. Additionally R3 must do ECMP for both the routes")
+
+ for addr_type in ADDR_TYPES:
+ next_hop_vrf = [
+ topo["routers"]["r1"]["links"]["r3-link3"][addr_type].\
+ split("/")[0],
+ topo["routers"]["r1"]["links"]["r3-link3"][addr_type]. \
+ split("/")[0]
+ ]
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_1[addr_type]],
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes,
+ next_hop=next_hop_vrf)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_locally_imported_routes_selected_as_bestpath_over_ebgp_imported_routes_p0(request):
+ """
+ Verify ECMP for imported routes from different VRFs.
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step("Configure same static routes on R2 and R3 vrfs and redistribute in BGP "
+ "for GREEN and RED vrf instances")
+ for dut, network in zip(["r2", "r3"], [
+ [NETWORK1_1, NETWORK1_2], [NETWORK1_1, NETWORK1_2]]):
+ for vrf_name, network_vrf in zip(["RED", "GREEN"], network):
+ step("Configure static route for VRF : {} on {}".format(vrf_name,
+ dut))
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [network_vrf[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ for dut, as_num in zip(["r2", "r3"], ["2", "3"]):
+ for vrf_name in ["RED", "GREEN"]:
+ step("Redistribute static route on BGP VRF : {}".format(vrf_name))
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({
+ addr_type: {
+ "unicast": {
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ })
+
+ redist_dict = {dut: {"bgp": [{
+ "vrf": vrf_name, "local_as": as_num, "address_family": temp
+ }]}}
+
+ result = create_router_bgp(tgen, topo, redist_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that R2 and R3 has installed redistributed routes in default "
+ "and RED vrfs and GREEN respectively:")
+ for dut, network in zip(["r2", "r3"],
+ [[NETWORK1_1, NETWORK1_2],
+ [NETWORK1_1, NETWORK1_2]]):
+ for vrf_name, network_vrf in zip(["RED", "GREEN"], network):
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ dut: {
+ "static_routes": [
+ {
+ "network": [network_vrf[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": vrf_name
+ }
+ ]
+ }
+ }
+ result = verify_bgp_rib(tgen, addr_type, dut, static_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ step("Import vrf RED's route in vrf GREEN on R3")
+ temp = {}
+ for addr_type in ADDR_TYPES:
+ temp.update({
+ addr_type: {
+ "unicast": {
+ "import": {
+ "vrf": "RED"
+ }
+ }
+ }
+ })
+
+ import_dict = {"r3": {"bgp": [{
+ "vrf": "GREEN", "local_as": 3, "address_family": temp
+ }]}}
+
+ result = create_router_bgp(tgen, topo, import_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that locally imported routes are installed over eBGP imported"
+ " routes from VRF RED into VRF GREEN")
+ for addr_type in ADDR_TYPES:
+ static_routes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": [NETWORK1_2[addr_type]],
+ "next_hop": "blackhole",
+ "vrf": "GREEN"
+ }
+ ]
+ }
+ }
+
+ input_routes = {
+ "r3": {
+ addr_type: [
+ {
+ "network": NETWORK1_2[addr_type],
+ "bestpath": BESTPATH[addr_type],
+ "vrf": "GREEN"
+ }
+ ]
+ }
+ }
+
+ result = verify_bgp_bestpath(tgen, addr_type, input_routes)
+ assert result is True, "Testcase {} : Failed \n Error {}". \
+ format(tc_name, result)
+
+ result = verify_rib(tgen, addr_type, "r3", static_routes)
+ 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/bgp_vrf_lite_ipv6_rtadv/__init__.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/__init__.py
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf
new file mode 100644
index 0000000..0033bc2
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/bgpd.conf
@@ -0,0 +1,15 @@
+router bgp 101 vrf r1-cust1
+ bgp router-id 10.254.254.1
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r1-eth0 interface peer-group r2g
+ neighbor r1-eth0 timers 3 10
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ address-family ipv6 unicast
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json
new file mode 100644
index 0000000..1c7500b
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv4_routes.json
@@ -0,0 +1,44 @@
+{
+ "10.254.254.2/32": [
+ {
+ "prefix": "10.254.254.2/32",
+ "protocol": "bgp",
+ "vrfName": "r1-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "r1-eth0",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "prefix": "10.254.254.1/32",
+ "protocol": "connected",
+ "vrfName": "r1-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "loop1",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json
new file mode 100644
index 0000000..0e1de87
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/ipv6_routes.json
@@ -0,0 +1,37 @@
+{
+ "2001:db8:1::/64": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "protocol": "bgp",
+ "vrfName": "r1-cust1",
+ "distance": 20,
+ "metric": 0,
+ "nexthops": [
+ {
+ "afi": "ipv6",
+ "interfaceName": "r1-eth0",
+ "active": true
+ }
+ ]
+ },
+ {
+ "prefix": "2001:db8:1::/64",
+ "protocol": "connected",
+ "vrfName": "r1-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "r1-eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf
new file mode 100644
index 0000000..74359a5
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r1/zebra.conf
@@ -0,0 +1,9 @@
+! debug zebra packet recv
+! debug zebra packet send
+log stdout
+interface loop1 vrf r1-cust1
+ ip address 10.254.254.1/32
+!
+interface r1-eth0 vrf r1-cust1
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf
new file mode 100644
index 0000000..183157e
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/bgpd.conf
@@ -0,0 +1,18 @@
+router bgp 102 vrf r2-cust1
+ bgp router-id 10.254.254.2
+ no bgp ebgp-requires-policy
+ neighbor r2g peer-group
+ neighbor r2g remote-as external
+ neighbor r2g bfd
+ neighbor r2-eth0 interface peer-group r2g
+ neighbor r2-eth0 timers 3 10
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ neighbor r2g activate
+ exit-address-family
+!
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json
new file mode 100644
index 0000000..4d7b289
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv4_routes.json
@@ -0,0 +1,44 @@
+{
+ "10.254.254.2/32": [
+ {
+ "prefix": "10.254.254.2/32",
+ "protocol": "connected",
+ "vrfName": "r2-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "loop1",
+ "active": true
+ }
+ ]
+ }
+ ],
+ "10.254.254.1/32": [
+ {
+ "prefix": "10.254.254.1/32",
+ "protocol": "bgp",
+ "vrfName": "r2-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 20,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "afi": "ipv6",
+ "interfaceName": "r2-eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json
new file mode 100644
index 0000000..805b57d
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/ipv6_routes.json
@@ -0,0 +1,23 @@
+{
+ "2001:db8:1::/64": [
+ {
+ "prefix": "2001:db8:1::/64",
+ "protocol": "connected",
+ "vrfName": "r2-cust1",
+ "selected": true,
+ "destSelected": true,
+ "distance": 0,
+ "metric": 0,
+ "installed": true,
+ "nexthops": [
+ {
+ "flags": 3,
+ "fib": true,
+ "directlyConnected": true,
+ "interfaceName": "r2-eth0",
+ "active": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf
new file mode 100644
index 0000000..c3795ab
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/r2/zebra.conf
@@ -0,0 +1,9 @@
+ip forwarding
+ipv6 forwarding
+!
+interface loop1 vrf r2-cust1
+ ip address 10.254.254.2/32
+!
+interface r2-eth0 vrf r2-cust1
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot
new file mode 100644
index 0000000..da67c29
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.dot
@@ -0,0 +1,44 @@
+## 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-topo2";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+}
diff --git a/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
new file mode 100644
index 0000000..b70e273
--- /dev/null
+++ b/tests/topotests/bgp_vrf_lite_ipv6_rtadv/test_bgp_vrf_lite_ipv6_rtadv.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_ipv6_rtadv.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+ test_bgp_ipv6_rtadv.py: Test the FRR BGP daemon with BGP IPv6 interface
+ with route advertisements on a separate netns.
+"""
+
+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
+from lib.common_config import required_linux_kernel_version
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers.
+ tgen.add_router("r1")
+ tgen.add_router("r2")
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("5.0")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ logger.info("Testing with VRF Lite support")
+
+ cmds = [
+ "ip link add {0}-cust1 type vrf table 1001",
+ "ip link add loop1 type dummy",
+ "ip link set loop1 master {0}-cust1",
+ "ip link set {0}-eth0 master {0}-cust1",
+ ]
+
+ for rname, router in router_list.items():
+ for cmd in cmds:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv4 routing tables.
+ logger.info("Checking IPv4 routes for convergence")
+
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv4_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip route vrf {}-cust1 json".format(router.name),
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ # Check IPv6 routing tables.
+ logger.info("Checking IPv6 routes for convergence")
+ for router in tgen.routers().values():
+ json_file = "{}/{}/ipv6_routes.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ipv6 route vrf {}-cust1 json".format(router.name),
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+
+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_vrf_netns/__init__.py b/tests/topotests/bgp_vrf_netns/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/__init__.py
diff --git a/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot
new file mode 100644
index 0000000..2b1f72b
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.dot
@@ -0,0 +1,50 @@
+## 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 bgp_vrf_netns_eBGP_topo1 {
+ label="bgp vrf netns topo1 - eBGP with different AS numbers";
+ labelloc="t";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.255.1/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # 1 Switch for eBGP Peers
+ s1 [
+ label="s1\n10.0.1.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # 1 ExaBGP Peers AS 101
+ peer1 [
+ label="eBGP peer1\nAS101\nrtr-id 10.0.1.101/32",
+ shape=rectangle,
+ fillcolor="#eee3d3",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="eth0\n.1"];
+
+ peer1 -- s1 [label="eth0\n.101"];
+
+ # Arrange network to make cleaner diagram
+ { rank=same peer1 } -- s1 -- { rank=same r1 } [style=invis]
+}
diff --git a/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf
new file mode 100644
index 0000000..6da2288
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/bgp-vrf-netns-topo.pdf
Binary files differ
diff --git a/tests/topotests/bgp_vrf_netns/exabgp.env b/tests/topotests/bgp_vrf_netns/exabgp.env
new file mode 100644
index 0000000..a328e04
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/exabgp.env
@@ -0,0 +1,54 @@
+
+[exabgp.api]
+encoder = text
+highres = false
+respawn = false
+socket = ''
+
+[exabgp.bgp]
+openwait = 60
+
+[exabgp.cache]
+attributes = true
+nexthops = true
+
+[exabgp.daemon]
+daemonize = true
+pid = '/var/run/exabgp/exabgp.pid'
+user = 'exabgp'
+##daemonize = false
+
+[exabgp.log]
+all = false
+configuration = true
+daemon = true
+destination = '/var/log/exabgp.log'
+enable = true
+level = INFO
+message = false
+network = true
+packets = false
+parser = false
+processes = true
+reactor = true
+rib = false
+routes = false
+short = false
+timers = false
+
+[exabgp.pdb]
+enable = false
+
+[exabgp.profile]
+enable = false
+file = ''
+
+[exabgp.reactor]
+speed = 1.0
+
+[exabgp.tcp]
+acl = false
+bind = ''
+delay = 0
+once = false
+port = 179
diff --git a/tests/topotests/bgp_vrf_netns/peer1/exa-send.py b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py
new file mode 100755
index 0000000..ab0eb8c
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/peer1/exa-send.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python2
+
+"""
+exa-send.py: Send a few testroutes with ExaBGP
+"""
+
+from sys import stdout, argv
+from time import sleep
+
+sleep(5)
+
+# 1st arg is peer number
+# 2nd arg is number of routes to send
+peer = int(argv[1])
+numRoutes = int(argv[2])
+asnum = 99
+
+# Announce numRoutes equal routes per PE - different neighbor AS
+for i in range(0, numRoutes):
+ stdout.write(
+ "announce route 10.201.%s.0/24 med 100 community %i:1 next-hop 10.0.%i.%i\n"
+ % (i, i, (((peer - 1) / 5) + 1), peer + 100)
+ )
+ stdout.flush()
+
+# Loop endlessly to allow ExaBGP to continue running
+while True:
+ sleep(1)
diff --git a/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg
new file mode 100644
index 0000000..2d0ca89
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/peer1/exabgp.cfg
@@ -0,0 +1,21 @@
+group controller {
+
+ process announce-routes {
+ run "/etc/exabgp/exa-send.py 1 10";
+ }
+
+ process receive-routes {
+ run "/etc/exabgp/exa-receive.py 1";
+ receive-routes;
+ encoder text;
+ }
+
+ neighbor 10.0.1.1 {
+ router-id 10.0.1.101;
+ local-address 10.0.1.101;
+ local-as 99;
+ peer-as 100;
+ graceful-restart;
+ }
+
+}
diff --git a/tests/topotests/bgp_vrf_netns/r1/bgpd.conf b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf
new file mode 100644
index 0000000..572dce7
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/r1/bgpd.conf
@@ -0,0 +1,10 @@
+!
+router bgp 100 vrf r1-bgp-cust1
+ bgp router-id 10.0.1.1
+ bgp bestpath as-path multipath-relax
+ no bgp ebgp-requires-policy
+ neighbor 10.0.1.101 remote-as 99
+ neighbor 10.0.1.101 timers 3 10
+ !
+!
+
diff --git a/tests/topotests/bgp_vrf_netns/r1/summary.txt b/tests/topotests/bgp_vrf_netns/r1/summary.txt
new file mode 100644
index 0000000..819f261
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/r1/summary.txt
@@ -0,0 +1,17 @@
+{
+"ipv4Unicast":{
+ "routerId":"10.0.1.1",
+ "as":100,
+ "vrfName":"r1-bgp-cust1",
+ "peerCount":1,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":10,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+}
+}
diff --git a/tests/topotests/bgp_vrf_netns/r1/summary20.txt b/tests/topotests/bgp_vrf_netns/r1/summary20.txt
new file mode 100644
index 0000000..ea04a56
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/r1/summary20.txt
@@ -0,0 +1,15 @@
+{
+ "routerId":"10.0.1.1",
+ "as":100,
+ "vrfName":"re1-bgp-cust1",
+ "peerCount":1,
+ "peers":{
+ "10.0.1.101":{
+ "outq":0,
+ "inq":0,
+ "pfxRcd":10,
+ "state":"Established"
+ }
+ },
+ "totalPeers":1
+}
diff --git a/tests/topotests/bgp_vrf_netns/r1/zebra.conf b/tests/topotests/bgp_vrf_netns/r1/zebra.conf
new file mode 100644
index 0000000..ed4edfa
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0 vrf r1-bgp-cust1
+ ip address 10.0.1.1/24
+!
+line vty
+!
+! debug vrf
diff --git a/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
new file mode 100644
index 0000000..c380cc1
--- /dev/null
+++ b/tests/topotests/bgp_vrf_netns/test_bgp_vrf_netns_topo.py
@@ -0,0 +1,225 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_vrf_netns_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2018 by 6WIND
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_vrf_netns_topo1.py: Test BGP topology with EBGP on NETNS VRF
+"""
+
+import json
+import os
+import sys
+import functools
+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.
+
+pytestmark = [pytest.mark.bgpd]
+
+
+total_ebgp_peers = 1
+CustomizeVrfWithNetns = True
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+ # Setup Switches
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Add eBGP ExaBGP neighbors
+ peer_ip = "10.0.1.101"
+ peer_route = "via 10.0.1.1"
+ peer = tgen.add_exabgp_peer("peer1", ip=peer_ip, defaultRoute=peer_route)
+ switch = tgen.gears["s1"]
+ switch.add_link(peer)
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # Get r1 reference
+ router = tgen.gears["r1"]
+
+ # check for zebra capability
+ if CustomizeVrfWithNetns == True:
+ if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False:
+ return pytest.skip(
+ "Skipping BGP VRF NETNS Test. VRF NETNS backend not available on FRR"
+ )
+ if os.system("ip netns list") != 0:
+ return pytest.skip(
+ "Skipping BGP VRF NETNS Test. NETNS not available on System"
+ )
+ # retrieve VRF backend kind
+ if CustomizeVrfWithNetns == True:
+ logger.info("Testing with VRF Namespace support")
+
+ # create VRF r1-bgp-cust1
+ # move r1-eth0 to VRF r1-bgp-cust1
+
+ ns = "{}-bgp-cust1".format("r1")
+ router.net.add_netns(ns)
+ router.net.set_intf_netns("r1-eth0", ns, up=True)
+
+ # run daemons
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format("r1")),
+ "--vrfwnetns",
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format("r1"))
+ )
+
+ logger.info("Launching BGP and ZEBRA")
+ # BGP and ZEBRA start without underlying VRF
+ router.start()
+ # Starting Hosts and init ExaBGP on each of them
+ logger.info("starting exaBGP on peer1")
+ peer_list = tgen.exabgp_peers()
+ for pname, peer in peer_list.items():
+ peer_dir = os.path.join(CWD, pname)
+ env_file = os.path.join(CWD, "exabgp.env")
+ logger.info("Running ExaBGP peer")
+ peer.start(peer_dir, env_file)
+ logger.info(pname)
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+
+ # Move interfaces out of vrf namespace and delete the namespace
+ tgen.net["r1"].reset_intf_netns("r1-eth0")
+ tgen.net["r1"].delete_netns("r1-bgp-cust1")
+
+ tgen.stop_topology()
+
+
+def test_bgp_vrf_learn():
+ "Test daemon learnt VRF context"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Expected result
+ output = tgen.gears["r1"].vtysh_cmd("show vrf", isjson=False)
+ logger.info("output is: {}".format(output))
+
+ output = tgen.gears["r1"].vtysh_cmd("show bgp vrfs", isjson=False)
+ logger.info("output is: {}".format(output))
+
+
+def test_bgp_convergence():
+ "Test for BGP topology convergence"
+ tgen = get_topogen()
+
+ # uncomment if you want to troubleshoot
+ # tgen.mininet_cli()
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for bgp convergence")
+
+ # Expected result
+ router = tgen.gears["r1"]
+ if router.has_version("<", "3.0"):
+ reffile = os.path.join(CWD, "r1/summary20.txt")
+ else:
+ reffile = os.path.join(CWD, "r1/summary.txt")
+
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show bgp vrf r1-bgp-cust1 summary json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=90, wait=0.5)
+ assertmsg = "BGP router network did not converge"
+ assert res is None, assertmsg
+
+
+def test_bgp_vrf_netns():
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ expect = {
+ "routerId": "10.0.1.1",
+ "routes": {},
+ }
+
+ for subnet in range(0, 10):
+ netkey = "10.201.{}.0/24".format(subnet)
+ expect["routes"][netkey] = []
+ peer = {"valid": True}
+ expect["routes"][netkey].append(peer)
+
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ tgen.gears["r1"],
+ "show ip bgp vrf r1-bgp-cust1 ipv4 json",
+ expect,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=12, wait=0.5)
+ assertmsg = 'expected routes in "show ip bgp vrf r1-bgp-cust1 ipv4" output'
+ assert res is None, assertmsg
+
+
+if __name__ == "__main__":
+
+ args = ["-s"] + sys.argv[1:]
+ ret = pytest.main(args)
+
+ sys.exit(ret)
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf
new file mode 100644
index 0000000..03dfbf9
--- /dev/null
+++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/bgpd.conf
@@ -0,0 +1,16 @@
+hostname r1
+
+router bgp 99 vrf DONNA
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ import vrf EVA
+ !
+!
+router bgp 99 vrf EVA
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ import vrf DONNA
+ !
+!
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf
new file mode 100644
index 0000000..3503855
--- /dev/null
+++ b/tests/topotests/bgp_vrf_route_leak_basic/r1/zebra.conf
@@ -0,0 +1,18 @@
+hostname r1
+
+int dummy1
+ ip address 10.0.0.1/24
+ no shut
+!
+int dummy2
+ ip address 10.0.1.1/24
+ no shut
+!
+int dummy3
+ ip address 10.0.2.1/24
+ no shut
+!
+int dummy4
+ ip address 10.0.3.1/24
+ no shut
+!
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs
new file mode 100644
index 0000000..fb67953
--- /dev/null
+++ b/tests/topotests/bgp_vrf_route_leak_basic/setup_vrfs
@@ -0,0 +1,16 @@
+#!/bin/bash
+
+ip link add DONNA type vrf table 1001
+ip link add EVA type vrf table 1002
+
+ip link add dummy1 type dummy
+ip link add dummy2 type dummy
+ip link add dummy3 type dummy
+ip link add dummy4 type dummy
+
+ip link set dummy1 master DONNA
+ip link set dummy2 master EVA
+ip link set dummy3 master DONNA
+ip link set dummy4 master EVA
+
+
diff --git a/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
new file mode 100644
index 0000000..191a0b5
--- /dev/null
+++ b/tests/topotests/bgp_vrf_route_leak_basic/test_bgp-vrf-route-leak-basic.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+
+#
+# test_bgp-vrf-route-leak-basic.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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 Cumulus Networks DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp-vrf-route-leak-basic.py.py: Test basic vrf route leaking
+"""
+
+import os
+import sys
+from functools import partial
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ for routern in range(1, 2):
+ tgen.add_router("r{}".format(routern))
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ router.run("/bin/bash {}/setup_vrfs".format(CWD))
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 test_vrf_route_leak():
+ logger.info("Ensure that routes are leaked back and forth")
+ 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"]
+
+ # Test DONNA VRF.
+ expect = {
+ "10.0.0.0/24": [
+ {
+ "protocol": "connected",
+ }
+ ],
+ "10.0.1.0/24": [
+ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
+ ],
+ "10.0.2.0/24": [{"protocol": "connected"}],
+ "10.0.3.0/24": [
+ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
+ ],
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf DONNA json", expect
+ )
+ result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result, "BGP VRF DONNA check failed:\n{}".format(diff)
+
+ # Test EVA VRF.
+ expect = {
+ "10.0.0.0/24": [
+ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
+ ],
+ "10.0.1.0/24": [
+ {
+ "protocol": "connected",
+ }
+ ],
+ "10.0.2.0/24": [
+ {"protocol": "bgp", "selected": True, "nexthops": [{"fib": True}]}
+ ],
+ "10.0.3.0/24": [
+ {
+ "protocol": "connected",
+ }
+ ],
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route vrf EVA json", expect
+ )
+ result, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assert result, "BGP VRF EVA check failed:\n{}".format(diff)
+
+
+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/config_timing/r1/staticd.conf b/tests/topotests/config_timing/r1/staticd.conf
new file mode 100644
index 0000000..0f9f97c
--- /dev/null
+++ b/tests/topotests/config_timing/r1/staticd.conf
@@ -0,0 +1 @@
+log timestamp precision 3
diff --git a/tests/topotests/config_timing/r1/zebra.conf b/tests/topotests/config_timing/r1/zebra.conf
new file mode 100644
index 0000000..46fd965
--- /dev/null
+++ b/tests/topotests/config_timing/r1/zebra.conf
@@ -0,0 +1,18 @@
+log timestamp precision 3
+
+ip prefix-list ANY permit 0.0.0.0/0 le 32
+ipv6 prefix-list ANY seq 10 permit any
+
+route-map RM-NONE4 deny 10
+exit-route-map
+
+route-map RM-NONE6 deny 10
+exit-route-map
+
+interface r1-eth0
+ ip address 100.0.0.1/24
+ ipv6 address 2102::1/64
+exit
+
+ip protocol static route-map RM-NONE4
+ipv6 protocol static route-map RM-NONE6
diff --git a/tests/topotests/config_timing/test_config_timing.py b/tests/topotests/config_timing/test_config_timing.py
new file mode 100644
index 0000000..7ab8619
--- /dev/null
+++ b/tests/topotests/config_timing/test_config_timing.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+#
+# June 2 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2019-2020 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 the timing of config operations.
+
+The initial add of 10k routes is used as a baseline for timing and all future
+operations are expected to complete in under 2 times that baseline. This is a
+lot of slop; however, the pre-batching code some of these operations (e.g.,
+adding the same set of 10k routes) would take 100 times longer, so the intention
+is to catch those types of regressions.
+"""
+
+import datetime
+import ipaddress
+import math
+import os
+import sys
+import pytest
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.staticd]
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_STATIC, os.path.join(CWD, "{}/staticd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def get_ip_networks(super_prefix, base_count, count):
+ count_log2 = math.log(base_count, 2)
+ if count_log2 != int(count_log2):
+ count_log2 = int(count_log2) + 1
+ else:
+ count_log2 = int(count_log2)
+ network = ipaddress.ip_network(super_prefix)
+ return tuple(network.subnets(count_log2))[0:count]
+
+
+def test_static_timing():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def do_config(
+ base_count,
+ count,
+ bad_indices,
+ base_delta,
+ d_multiplier,
+ add=True,
+ do_ipv6=False,
+ super_prefix=None,
+ en_dbg=False,
+ ):
+ router_list = tgen.routers()
+ tot_delta = float(0)
+
+ optype = "adding" if add else "removing"
+ iptype = "IPv6" if do_ipv6 else "IPv4"
+ if super_prefix is None:
+ super_prefix = u"2001::/48" if do_ipv6 else u"10.0.0.0/8"
+ via = u"lo"
+ optyped = "added" if add else "removed"
+
+ for rname, router in router_list.items():
+ router.logger.info("{} {} static {} routes".format(optype, count, iptype))
+
+ # Generate config file.
+ config_file = os.path.join(
+ router.logdir, rname, "{}-routes-{}.conf".format(iptype.lower(), optype)
+ )
+ with open(config_file, "w") as f:
+ for i, net in enumerate(get_ip_networks(super_prefix, base_count, count)):
+ if i in bad_indices:
+ if add:
+ f.write("ip route {} {} bad_input\n".format(net, via))
+ else:
+ f.write("no ip route {} {} bad_input\n".format(net, via))
+ elif add:
+ f.write("ip route {} {}\n".format(net, via))
+ else:
+ f.write("no ip route {} {}\n".format(net, via))
+
+ # Enable debug
+ if en_dbg:
+ router.vtysh_cmd("debug northbound callbacks configuration")
+
+ # Load config file.
+ load_command = 'vtysh -f "{}"'.format(config_file)
+ tstamp = datetime.datetime.now()
+ output = router.run(load_command)
+ delta = (datetime.datetime.now() - tstamp).total_seconds()
+ tot_delta += delta
+
+ router.logger.info(
+ "\nvtysh command => {}\nvtysh output <= {}\nin {}s".format(
+ load_command, output, delta
+ )
+ )
+
+ limit_delta = base_delta * d_multiplier
+ logger.info(
+ "{} {} {} static routes under {} in {}s (limit: {}s)".format(
+ optyped, count, iptype.lower(), super_prefix, tot_delta, limit_delta
+ )
+ )
+ if limit_delta:
+ assert tot_delta <= limit_delta
+
+ return tot_delta
+
+ # Number of static routes
+ prefix_count = 10000
+ prefix_base = [
+ [u"10.0.0.0/8", u"11.0.0.0/8"],
+ [u"2100:1111:2220::/44", u"2100:3333:4440::/44"],
+ ]
+
+ bad_indices = []
+ for ipv6 in [False, True]:
+ base_delta = do_config(
+ prefix_count, prefix_count, bad_indices, 0, 0, True, ipv6, prefix_base[ipv6][0]
+ )
+
+ # Another set of same number of prefixes
+ do_config(
+ prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][1]
+ )
+
+ # Duplicate config
+ do_config(
+ prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
+ )
+
+ # Remove 1/2 of duplicate
+ do_config(
+ prefix_count,
+ prefix_count // 2,
+ bad_indices,
+ base_delta,
+ 3,
+ False,
+ ipv6,
+ prefix_base[ipv6][0],
+ )
+
+ # Add all back in so 1/2 replicate 1/2 new
+ do_config(
+ prefix_count, prefix_count, bad_indices, base_delta, 3, True, ipv6, prefix_base[ipv6][0]
+ )
+
+ # remove all
+ delta = do_config(
+ prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][0]
+ )
+ delta += do_config(
+ prefix_count, prefix_count, bad_indices, base_delta, 3, False, ipv6, prefix_base[ipv6][1]
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/conftest.py b/tests/topotests/conftest.py
new file mode 100755
index 0000000..2a57f6c
--- /dev/null
+++ b/tests/topotests/conftest.py
@@ -0,0 +1,537 @@
+"""
+Topotest conftest.py file.
+"""
+# pylint: disable=consider-using-f-string
+
+import glob
+import os
+import pdb
+import re
+import subprocess
+import sys
+import time
+import resource
+
+import pytest
+import lib.fixtures
+from lib import topolog
+from lib.micronet import Commander, proc_error
+from lib.micronet_cli import cli
+from lib.micronet_compat import Mininet, cleanup_current, cleanup_previous
+from lib.topogen import diagnose_env, get_topogen
+from lib.topolog import logger
+from lib.topotest import g_extra_config as topotest_extra_config
+from lib.topotest import json_cmp_result
+
+
+def pytest_addoption(parser):
+ """
+ Add topology-only option to the topology tester. This option makes pytest
+ only run the setup_module() to setup the topology without running any tests.
+ """
+ parser.addoption(
+ "--asan-abort",
+ action="store_true",
+ help="Configure address sanitizer to abort process on error",
+ )
+
+ parser.addoption(
+ "--cli-on-error",
+ action="store_true",
+ help="Mininet cli on test failure",
+ )
+
+ parser.addoption(
+ "--gdb-breakpoints",
+ metavar="SYMBOL[,SYMBOL...]",
+ help="Comma-separated list of functions to set gdb breakpoints on",
+ )
+
+ parser.addoption(
+ "--gdb-daemons",
+ metavar="DAEMON[,DAEMON...]",
+ help="Comma-separated list of daemons to spawn gdb on, or 'all'",
+ )
+
+ parser.addoption(
+ "--gdb-routers",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn gdb on, or 'all'",
+ )
+
+ parser.addoption(
+ "--pause",
+ action="store_true",
+ help="Pause after each test",
+ )
+
+ parser.addoption(
+ "--pause-on-error",
+ action="store_true",
+ help="Do not pause after (disables default when --shell or -vtysh given)",
+ )
+
+ parser.addoption(
+ "--no-pause-on-error",
+ dest="pause_on_error",
+ action="store_false",
+ help="Do not pause after (disables default when --shell or -vtysh given)",
+ )
+
+ rundir_help = "directory for running in and log files"
+ parser.addini("rundir", rundir_help, default="/tmp/topotests")
+ parser.addoption("--rundir", metavar="DIR", help=rundir_help)
+
+ parser.addoption(
+ "--shell",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn shell on, or 'all'",
+ )
+
+ parser.addoption(
+ "--shell-on-error",
+ action="store_true",
+ help="Spawn shell on all routers on test failure",
+ )
+
+ parser.addoption(
+ "--strace-daemons",
+ metavar="DAEMON[,DAEMON...]",
+ help="Comma-separated list of daemons to strace, or 'all'",
+ )
+
+ parser.addoption(
+ "--topology-only",
+ action="store_true",
+ default=False,
+ help="Only set up this topology, don't run tests",
+ )
+
+ parser.addoption(
+ "--valgrind-extra",
+ action="store_true",
+ help="Generate suppression file, and enable more precise (slower) valgrind checks",
+ )
+
+ parser.addoption(
+ "--valgrind-memleaks",
+ action="store_true",
+ help="Run all daemons under valgrind for memleak detection",
+ )
+
+ parser.addoption(
+ "--vtysh",
+ metavar="ROUTER[,ROUTER...]",
+ help="Comma-separated list of routers to spawn vtysh on, or 'all'",
+ )
+
+ parser.addoption(
+ "--vtysh-on-error",
+ action="store_true",
+ help="Spawn vtysh on all routers on test failure",
+ )
+
+
+def check_for_memleaks():
+ assert topotest_extra_config["valgrind_memleaks"]
+
+ leaks = []
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ latest = []
+ existing = []
+ if tgen is not None:
+ logdir = tgen.logdir
+ if hasattr(tgen, "valgrind_existing_files"):
+ existing = tgen.valgrind_existing_files
+ latest = glob.glob(os.path.join(logdir, "*.valgrind.*"))
+ latest = [x for x in latest if "core" not in x]
+
+ daemons = set()
+ for vfile in latest:
+ if vfile in existing:
+ continue
+ # do not consider memleaks from parent fork (i.e., owned by root)
+ if os.stat(vfile).st_uid == 0:
+ existing.append(vfile) # do not check again
+ logger.debug("Skipping valgrind file %s owned by root", vfile)
+ continue
+ logger.debug("Checking valgrind file %s not owned by root", vfile)
+ with open(vfile, encoding="ascii") as vf:
+ vfcontent = vf.read()
+ match = re.search(r"ERROR SUMMARY: (\d+) errors", vfcontent)
+ if match:
+ existing.append(vfile) # have summary don't check again
+ if match and match.group(1) != "0":
+ emsg = "{} in {}".format(match.group(1), vfile)
+ leaks.append(emsg)
+ daemon = re.match(r".*\.valgrind\.(.*)\.\d+", vfile).group(1)
+ daemons.add("{}({})".format(daemon, match.group(1)))
+
+ if tgen is not None:
+ tgen.valgrind_existing_files = existing
+
+ if leaks:
+ logger.error("valgrind memleaks found:\n\t%s", "\n\t".join(leaks))
+ pytest.fail("valgrind memleaks found for daemons: " + " ".join(daemons))
+
+
+@pytest.fixture(autouse=True, scope="module")
+def module_check_memtest(request):
+ del request # disable unused warning
+ yield
+ if topotest_extra_config["valgrind_memleaks"]:
+ if get_topogen() is not None:
+ check_for_memleaks()
+
+
+def pytest_runtest_logstart(nodeid, location):
+ # location is (filename, lineno, testname)
+ topolog.logstart(nodeid, location, topotest_extra_config["rundir"])
+
+
+def pytest_runtest_logfinish(nodeid, location):
+ # location is (filename, lineno, testname)
+ topolog.logfinish(nodeid, location)
+
+
+@pytest.hookimpl(hookwrapper=True)
+def pytest_runtest_call(item: pytest.Item) -> None:
+ "Hook the function that is called to execute the test."
+ del item # disable unused warning
+
+ # For topology only run the CLI then exit
+ if topotest_extra_config["topology_only"]:
+ get_topogen().cli()
+ pytest.exit("exiting after --topology-only")
+
+ # Let the default pytest_runtest_call execute the test function
+ yield
+
+ # Check for leaks if requested
+ if topotest_extra_config["valgrind_memleaks"]:
+ check_for_memleaks()
+
+
+def pytest_assertrepr_compare(op, left, right):
+ """
+ Show proper assertion error message for json_cmp results.
+ """
+ del op
+
+ json_result = left
+ if not isinstance(json_result, json_cmp_result):
+ json_result = right
+ if not isinstance(json_result, json_cmp_result):
+ return None
+
+ return json_result.gen_report()
+
+
+def pytest_configure(config):
+ """
+ Assert that the environment is correctly configured, and get extra config.
+ """
+
+ if config.getoption("--collect-only"):
+ return
+
+ if "PYTEST_XDIST_WORKER" not in os.environ:
+ os.environ["PYTEST_XDIST_MODE"] = config.getoption("dist", "no")
+ os.environ["PYTEST_TOPOTEST_WORKER"] = ""
+ is_xdist = os.environ["PYTEST_XDIST_MODE"] != "no"
+ is_worker = False
+ else:
+ os.environ["PYTEST_TOPOTEST_WORKER"] = os.environ["PYTEST_XDIST_WORKER"]
+ is_xdist = True
+ is_worker = True
+
+ resource.setrlimit(
+ resource.RLIMIT_CORE, (resource.RLIM_INFINITY, resource.RLIM_INFINITY)
+ )
+ # -----------------------------------------------------
+ # Set some defaults for the pytest.ini [pytest] section
+ # ---------------------------------------------------
+
+ rundir = config.getoption("--rundir")
+ if not rundir:
+ rundir = config.getini("rundir")
+ if not rundir:
+ rundir = "/tmp/topotests"
+ if not config.getoption("--junitxml"):
+ config.option.xmlpath = os.path.join(rundir, "topotests.xml")
+ xmlpath = config.option.xmlpath
+
+ # Save an existing topotest.xml
+ if os.path.exists(xmlpath):
+ fmtime = time.localtime(os.path.getmtime(xmlpath))
+ suffix = "-" + time.strftime("%Y%m%d%H%M%S", fmtime)
+ commander = Commander("pytest")
+ mv_path = commander.get_exec_path("mv")
+ commander.cmd_status([mv_path, xmlpath, xmlpath + suffix])
+
+ topotest_extra_config["rundir"] = rundir
+
+ # Set the log_file (exec) to inside the rundir if not specified
+ if not config.getoption("--log-file") and not config.getini("log_file"):
+ config.option.log_file = os.path.join(rundir, "exec.log")
+
+ # Turn on live logging if user specified verbose and the config has a CLI level set
+ if config.getoption("--verbose") and not is_xdist and not config.getini("log_cli"):
+ if config.getoption("--log-cli-level", None) is None:
+ # By setting the CLI option to the ini value it enables log_cli=1
+ cli_level = config.getini("log_cli_level")
+ if cli_level is not None:
+ config.option.log_cli_level = cli_level
+
+ have_tmux = bool(os.getenv("TMUX", ""))
+ have_screen = not have_tmux and bool(os.getenv("STY", ""))
+ have_xterm = not have_tmux and not have_screen and bool(os.getenv("DISPLAY", ""))
+ have_windows = have_tmux or have_screen or have_xterm
+ have_windows_pause = have_tmux or have_xterm
+ xdist_no_windows = is_xdist and not is_worker and not have_windows_pause
+
+ def assert_feature_windows(b, feature):
+ if b and xdist_no_windows:
+ pytest.exit(
+ "{} use requires byobu/TMUX/XTerm under dist {}".format(
+ feature, os.environ["PYTEST_XDIST_MODE"]
+ )
+ )
+ elif b and not is_xdist and not have_windows:
+ pytest.exit("{} use requires byobu/TMUX/SCREEN/XTerm".format(feature))
+
+ # ---------------------------------------
+ # Record our options in global dictionary
+ # ---------------------------------------
+
+ topotest_extra_config["rundir"] = rundir
+
+ asan_abort = config.getoption("--asan-abort")
+ topotest_extra_config["asan_abort"] = asan_abort
+
+ gdb_routers = config.getoption("--gdb-routers")
+ gdb_routers = gdb_routers.split(",") if gdb_routers else []
+ topotest_extra_config["gdb_routers"] = gdb_routers
+
+ gdb_daemons = config.getoption("--gdb-daemons")
+ gdb_daemons = gdb_daemons.split(",") if gdb_daemons else []
+ topotest_extra_config["gdb_daemons"] = gdb_daemons
+ assert_feature_windows(gdb_routers or gdb_daemons, "GDB")
+
+ gdb_breakpoints = config.getoption("--gdb-breakpoints")
+ gdb_breakpoints = gdb_breakpoints.split(",") if gdb_breakpoints else []
+ topotest_extra_config["gdb_breakpoints"] = gdb_breakpoints
+
+ cli_on_error = config.getoption("--cli-on-error")
+ topotest_extra_config["cli_on_error"] = cli_on_error
+ assert_feature_windows(cli_on_error, "--cli-on-error")
+
+ shell = config.getoption("--shell")
+ topotest_extra_config["shell"] = shell.split(",") if shell else []
+ assert_feature_windows(shell, "--shell")
+
+ strace = config.getoption("--strace-daemons")
+ topotest_extra_config["strace_daemons"] = strace.split(",") if strace else []
+
+ shell_on_error = config.getoption("--shell-on-error")
+ topotest_extra_config["shell_on_error"] = shell_on_error
+ assert_feature_windows(shell_on_error, "--shell-on-error")
+
+ topotest_extra_config["valgrind_extra"] = config.getoption("--valgrind-extra")
+ topotest_extra_config["valgrind_memleaks"] = config.getoption("--valgrind-memleaks")
+
+ vtysh = config.getoption("--vtysh")
+ topotest_extra_config["vtysh"] = vtysh.split(",") if vtysh else []
+ assert_feature_windows(vtysh, "--vtysh")
+
+ vtysh_on_error = config.getoption("--vtysh-on-error")
+ topotest_extra_config["vtysh_on_error"] = vtysh_on_error
+ assert_feature_windows(vtysh_on_error, "--vtysh-on-error")
+
+ pause_on_error = vtysh or shell or config.getoption("--pause-on-error")
+ if config.getoption("--no-pause-on-error"):
+ pause_on_error = False
+
+ topotest_extra_config["pause_on_error"] = pause_on_error
+ assert_feature_windows(pause_on_error, "--pause-on-error")
+
+ pause = config.getoption("--pause")
+ topotest_extra_config["pause"] = pause
+ assert_feature_windows(pause, "--pause")
+
+ topology_only = config.getoption("--topology-only")
+ if topology_only and is_xdist:
+ pytest.exit("Cannot use --topology-only with distributed test mode")
+ topotest_extra_config["topology_only"] = topology_only
+
+ # Check environment now that we have config
+ if not diagnose_env(rundir):
+ pytest.exit("environment has errors, please read the logs in %s" % rundir)
+
+
+@pytest.fixture(autouse=True, scope="session")
+def setup_session_auto():
+ if "PYTEST_TOPOTEST_WORKER" not in os.environ:
+ is_worker = False
+ elif not os.environ["PYTEST_TOPOTEST_WORKER"]:
+ is_worker = False
+ else:
+ is_worker = True
+
+ logger.debug("Before the run (is_worker: %s)", is_worker)
+ if not is_worker:
+ cleanup_previous()
+ yield
+ if not is_worker:
+ cleanup_current()
+ logger.debug("After the run (is_worker: %s)", is_worker)
+
+
+def pytest_runtest_setup(item):
+ module = item.parent.module
+ script_dir = os.path.abspath(os.path.dirname(module.__file__))
+ os.environ["PYTEST_TOPOTEST_SCRIPTDIR"] = script_dir
+
+
+def pytest_runtest_makereport(item, call):
+ "Log all assert messages to default logger with error level"
+
+ # Nothing happened
+ if call.when == "call":
+ pause = topotest_extra_config["pause"]
+ else:
+ pause = False
+
+ title = "unset"
+
+ if call.excinfo is None:
+ error = False
+ else:
+ parent = item.parent
+ modname = parent.module.__name__
+
+ # Treat skips as non errors, don't pause after
+ if call.excinfo.typename == "Skipped":
+ pause = False
+ error = False
+ logger.info(
+ 'test skipped at "{}/{}": {}'.format(
+ modname, item.name, call.excinfo.value
+ )
+ )
+ else:
+ error = True
+ # Handle assert failures
+ parent._previousfailed = item # pylint: disable=W0212
+ logger.error(
+ 'test failed at "{}/{}": {}'.format(
+ modname, item.name, call.excinfo.value
+ )
+ )
+ title = "{}/{}".format(modname, item.name)
+
+ # We want to pause, if requested, on any error not just test cases
+ # (e.g., call.when == "setup")
+ if not pause:
+ pause = (
+ topotest_extra_config["pause_on_error"]
+ or topotest_extra_config["pause"]
+ )
+
+ # (topogen) Set topology error to avoid advancing in the test.
+ tgen = get_topogen() # pylint: disable=redefined-outer-name
+ if tgen is not None:
+ # This will cause topogen to report error on `routers_have_failure`.
+ tgen.set_error("{}/{}".format(modname, item.name))
+
+ commander = Commander("pytest")
+ isatty = sys.stdout.isatty()
+ error_cmd = None
+
+ if error and topotest_extra_config["vtysh_on_error"]:
+ error_cmd = commander.get_exec_path(["vtysh"])
+ elif error and topotest_extra_config["shell_on_error"]:
+ error_cmd = os.getenv("SHELL", commander.get_exec_path(["bash"]))
+
+ if error_cmd:
+ is_tmux = bool(os.getenv("TMUX", ""))
+ is_screen = not is_tmux and bool(os.getenv("STY", ""))
+ is_xterm = not is_tmux and not is_screen and bool(os.getenv("DISPLAY", ""))
+
+ channel = None
+ win_info = None
+ wait_for_channels = []
+ wait_for_procs = []
+ # Really would like something better than using this global here.
+ # Not all tests use topogen though so get_topogen() won't work.
+ for node in Mininet.g_mnet_inst.hosts.values():
+ pause = True
+
+ if is_tmux:
+ channel = (
+ "{}-{}".format(os.getpid(), Commander.tmux_wait_gen)
+ if not isatty
+ else None
+ )
+ Commander.tmux_wait_gen += 1
+ wait_for_channels.append(channel)
+
+ pane_info = node.run_in_window(
+ error_cmd,
+ new_window=win_info is None,
+ background=True,
+ title="{} ({})".format(title, node.name),
+ name=title,
+ tmux_target=win_info,
+ wait_for=channel,
+ )
+ if is_tmux:
+ if win_info is None:
+ win_info = pane_info
+ elif is_xterm:
+ assert isinstance(pane_info, subprocess.Popen)
+ wait_for_procs.append(pane_info)
+
+ # Now wait on any channels
+ for channel in wait_for_channels:
+ logger.debug("Waiting on TMUX channel %s", channel)
+ commander.cmd_raises([commander.get_exec_path("tmux"), "wait", channel])
+ for p in wait_for_procs:
+ logger.debug("Waiting on TMUX xterm process %s", p)
+ o, e = p.communicate()
+ if p.wait():
+ logger.warning("xterm proc failed: %s:", proc_error(p, o, e))
+
+ if error and topotest_extra_config["cli_on_error"]:
+ # Really would like something better than using this global here.
+ # Not all tests use topogen though so get_topogen() won't work.
+ if Mininet.g_mnet_inst:
+ cli(Mininet.g_mnet_inst, title=title, background=False)
+ else:
+ logger.error("Could not launch CLI b/c no mininet exists yet")
+
+ while pause and isatty:
+ try:
+ user = raw_input(
+ 'PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: '
+ )
+ except NameError:
+ user = input('PAUSED, "cli" for CLI, "pdb" to debug, "Enter" to continue: ')
+ user = user.strip()
+
+ if user == "cli":
+ cli(Mininet.g_mnet_inst)
+ elif user == "pdb":
+ pdb.set_trace() # pylint: disable=forgotten-debug-statement
+ elif user:
+ print('Unrecognized input: "%s"' % user)
+ else:
+ break
+
+
+#
+# Add common fixtures available to all tests as parameters
+#
+
+tgen = pytest.fixture(lib.fixtures.tgen)
+topo = pytest.fixture(lib.fixtures.topo)
diff --git a/tests/topotests/cspf_topo1/r1/isisd.conf b/tests/topotests/cspf_topo1/r1/isisd.conf
new file mode 100644
index 0000000..788ac5b
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/isisd.conf
@@ -0,0 +1,33 @@
+!
+hostname r1
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r1-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r1-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis TE
+ net 49.0000.0000.0000.0001.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.1
+ mpls-te router-address ipv6 2001:db8::1
+ mpls-te export
+!
+
diff --git a/tests/topotests/cspf_topo1/r1/sharpd.conf b/tests/topotests/cspf_topo1/r1/sharpd.conf
new file mode 100644
index 0000000..272eac9
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/sharpd.conf
@@ -0,0 +1,3 @@
+!
+import-te
+!
diff --git a/tests/topotests/cspf_topo1/r1/zebra.conf b/tests/topotests/cspf_topo1/r1/zebra.conf
new file mode 100644
index 0000000..3aa0cad
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r1/zebra.conf
@@ -0,0 +1,27 @@
+!
+hostname r1
+!
+interface lo
+ ip address 10.0.255.1/32
+ ipv6 address 2001:db8::1/128
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ link-params
+ metric 20
+ delay 10000
+ ava-bw 1.25e+08
+ enable
+ exit-link-params
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8:1::1:1/64
+ link-params
+ metric 10
+ delay 20000
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/cspf_topo1/r2/isisd.conf b/tests/topotests/cspf_topo1/r2/isisd.conf
new file mode 100644
index 0000000..04df685
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r2/isisd.conf
@@ -0,0 +1,46 @@
+!
+hostname r2
+!
+! debug isis te-events
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r2-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth2
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth3
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis TE
+ net 49.0000.0000.0000.0002.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.2
+ mpls-te router-address ipv6 2001:db8::2
+!
diff --git a/tests/topotests/cspf_topo1/r2/zebra.conf b/tests/topotests/cspf_topo1/r2/zebra.conf
new file mode 100644
index 0000000..1cc37ba
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r2/zebra.conf
@@ -0,0 +1,45 @@
+!
+hostname r2
+!
+interface lo
+ ip address 10.0.255.2/32
+ ipv6 address 2001:db8::2/128
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+ link-params
+ metric 20
+ delay 10000
+ enable
+ exit-link-params
+!
+interface r2-eth1
+ ip address 10.0.1.2/24
+ ipv6 address 2001:db8:1::1:2/64
+ link-params
+ metric 10
+ delay 20000
+ enable
+ exit-link-params
+!
+interface r2-eth2
+ ip address 10.0.3.2/24
+ ipv6 address 2001:db8:3::3:2/64
+ link-params
+ metric 40
+ delay 40000
+ enable
+ exit-link-params
+!
+interface r2-eth3
+ ip address 10.0.4.2/24
+ ipv6 address 2001:db8:4::4:2/64
+ link-params
+ metric 25
+ delay 25000
+ use-bw 1.25e+8
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/cspf_topo1/r3/isisd.conf b/tests/topotests/cspf_topo1/r3/isisd.conf
new file mode 100644
index 0000000..9db82c7
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r3/isisd.conf
@@ -0,0 +1,34 @@
+!
+hostname r3
+!
+! debug isis te-events
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r3-eth0
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r3-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+!
+router isis TE
+ net 49.0000.0000.0000.0003.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.3
+ mpls-te router-address ipv6 2001:db8::3
+!
diff --git a/tests/topotests/cspf_topo1/r3/zebra.conf b/tests/topotests/cspf_topo1/r3/zebra.conf
new file mode 100644
index 0000000..29a4c51
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r3/zebra.conf
@@ -0,0 +1,27 @@
+!
+hostname r3
+!
+interface lo
+ ip address 10.0.255.3/32
+ ipv6 address 2001:db8::3/128
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+ ipv6 address 2001:db8:3::3:3/64
+ link-params
+ metric 25
+ delay 25000
+ enable
+ admin-grp 0x20
+ exit-link-params
+!
+interface r3-eth1
+ ipv6 address 2001:db8:5::4:3/64
+ link-params
+ enable
+ metric 10
+ delay 10000
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/cspf_topo1/r4/isisd.conf b/tests/topotests/cspf_topo1/r4/isisd.conf
new file mode 100644
index 0000000..c5c4d4e
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r4/isisd.conf
@@ -0,0 +1,40 @@
+!
+hostname r4
+!
+! debug isis te-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r4-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r4-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+!
+router isis TE
+ net 49.0000.0000.0000.0004.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.4
+ mpls-te router-address ipv6 2001:db8::4
+ segment-routing on
+ segment-routing global-block 10000 19999 local-block 5000 5999
+ segment-routing node-msd 12
+ segment-routing prefix 10.0.255.4/32 index 400 no-php-flag
+ segment-routing prefix 2001:db8:ffff::4/128 index 1400 no-php-flag
+!
diff --git a/tests/topotests/cspf_topo1/r4/zebra.conf b/tests/topotests/cspf_topo1/r4/zebra.conf
new file mode 100644
index 0000000..bf5306d
--- /dev/null
+++ b/tests/topotests/cspf_topo1/r4/zebra.conf
@@ -0,0 +1,26 @@
+!
+hostname r4
+!
+interface lo
+ ip address 10.0.255.4/32
+ ipv6 address 2001:db8::4/128
+!
+interface r4-eth0
+ ip address 10.0.4.4/24
+ ipv6 address 2001:db8:4::2:4/64
+ link-params
+ metric 40
+ delay 40000
+ enable
+ exit-link-params
+!
+interface r4-eth1
+ ipv6 address 2001:db8:5::3:4/64
+ link-params
+ metric 10
+ delay 10000
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt
new file mode 100644
index 0000000..df792ce
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-failed-dst.txt
@@ -0,0 +1 @@
+Path computation failed: 2
diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt
new file mode 100644
index 0000000..48f6fd9
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-failed-same.txt
@@ -0,0 +1 @@
+Path computation failed: 3
diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt b/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt
new file mode 100644
index 0000000..62d00cc
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-failed-src.txt
@@ -0,0 +1 @@
+Path computation failed: 1
diff --git a/tests/topotests/cspf_topo1/reference/cspf-failed.txt b/tests/topotests/cspf_topo1/reference/cspf-failed.txt
new file mode 100644
index 0000000..de53a93
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-failed.txt
@@ -0,0 +1 @@
+Path computation failed: 0
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt
new file mode 100644
index 0000000..2cc0428
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-delay.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 35000
+ Edges: 10.0.0.2 10.0.4.4
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt
new file mode 100644
index 0000000..060a39c
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-metric.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 20
+ Edges: 10.0.0.2 10.0.4.4
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt
new file mode 100644
index 0000000..6d98306
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv4-te-metric.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 35
+ Edges: 10.0.1.2 10.0.4.4
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt
new file mode 100644
index 0000000..b65869b
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-delay.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 70000
+ Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt
new file mode 100644
index 0000000..5acbb74
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-metric.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 30
+ Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4
diff --git a/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt b/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt
new file mode 100644
index 0000000..2290a04
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/cspf-ipv6-te-metric.txt
@@ -0,0 +1,3 @@
+Path computation success
+ Cost: 60
+ Edges: 2001:db8:1::1:2 2001:db8:3::3:3 2001:db8:5::3:4
diff --git a/tests/topotests/cspf_topo1/reference/sharp-ted.json b/tests/topotests/cspf_topo1/reference/sharp-ted.json
new file mode 100644
index 0000000..d3d1f9e
--- /dev/null
+++ b/tests/topotests/cspf_topo1/reference/sharp-ted.json
@@ -0,0 +1,858 @@
+{
+ "ted":{
+ "name":"Sharp",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":14,
+ "subnetsCount":22,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1",
+ "router-id-v6":"2001:db8::1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2",
+ "router-id-v6":"2001:db8::2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "router-id-v6":"2001:db8::4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":65537,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:1::1:1",
+ "remote-address-v6":"2001:db8:1::1:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":65538,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":40,
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":40000
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":25,
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000
+ },
+ "segment-routing":[
+ {
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":40,
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":40000
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":25,
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":25,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":40,
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":40000
+ },
+ "segment-routing":[
+ {
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8:1::1:1/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:2/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::1/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::2/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::3/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::4/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/cspf_topo1/test_cspf_topo1.py b/tests/topotests/cspf_topo1/test_cspf_topo1.py
new file mode 100644
index 0000000..1b71ac3
--- /dev/null
+++ b/tests/topotests/cspf_topo1/test_cspf_topo1.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python
+
+#
+# test_cspf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2022 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+# 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_cspf_topo1.py: Test the FRR Constraint Shortest Path First algorithm.
+
+ +------------+
+ | |
+ | R1 |
+ | 10.0.225.1 |
+ | |
+ +------------+
+ r1-eth0| |r1-eth1
+ | |
+ 10.0.0.0/24| |10.0.1.0/24
+ | |2001:db8:1:/64
+ | |
+ r2-eth0| |r2-eth1
+ +------------+ +------------+
+ | | | |
+ | R2 |r2-eth2 r3-eth0| R3 |
+ | 10.0.255.2 +------------------+ 10.0.255.3 |
+ | | 10.0.3.0/24 | |
+ +------------+ 2001:db8:3:/64 +------+-----+
+ r2-eth3| r3-eth1|
+ | |
+ 10.0.4.0/24| |
+ | |
+ | |
+ r4-eth0| 2001:db8:5:/64|
+ +------------+ |
+ | | |
+ | R4 |r4-eth1 |
+ | 10.0.255.4 +-------------------------+
+ | |
+ +------------+
+
+"""
+
+import os
+import sys
+import json
+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
+
+# and Finally pytest
+import pytest
+
+pytestmark = [pytest.mark.isisd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Interconect router 1 and 2 with 2 links
+ 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["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 3 and 2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 4 and 2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconnect router 3 and 4
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ logger.info("\n\n---- Starting CSPF tests ----\n")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ if rname == "r1":
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format("r1"))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module():
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+ logger.info("\n\n---- CSPF tests End ----\n")
+
+
+def compare_ted_json_output(tgen, rname, fileref):
+ "Compare TED JSON output"
+
+ logger.info('Comparing router "%s" TED output', rname)
+
+ filename = "{}/reference/{}".format(CWD, fileref)
+ expected = json.loads(open(filename).read())
+ command = "show sharp ted json"
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def compare_cspf_output(tgen, rname, fileref, src, dst, cost, bw=""):
+ "Compare CSPF output"
+
+ logger.info('Comparing router "%s" CSPF output', rname)
+
+ filename = "{}/reference/{}".format(CWD, fileref)
+ expected = open(filename).read()
+ command = "show sharp cspf source {} destination {} {} {}".format(src, dst, cost, bw)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_output_cmp, tgen.gears[rname], command, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=2, wait=2)
+ assert result, "CSPF output mismatches the expected result on {}:\n{}".format(rname, diff)
+
+
+def setup_testcase(msg):
+ "Setup test case"
+
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+# Note that all routers must discover the same Network Topology, so the same TED.
+
+
+def test_step1():
+ "Step1: Check initial topology"
+
+ tgen = setup_testcase("Step1: test initial IS-IS TE Data Base import")
+ tgen.net["r1"].cmd('vtysh -c "sharp import-te"')
+
+ compare_ted_json_output(tgen, "r1", "sharp-ted.json")
+
+
+def test_step2():
+ "Step2: Test CSPF from r1 to r4 for IPv4 with various metric"
+
+ tgen = setup_testcase("Step2: CSPF(r1, r4, IPv4)")
+
+ compare_cspf_output(tgen, "r1", "cspf-ipv4-metric.txt", "10.0.0.1", "10.0.255.4", "metric 50")
+ compare_cspf_output(tgen, "r1", "cspf-ipv4-te-metric.txt", "10.0.255.1", "10.0.4.4", "te-metric 50")
+ compare_cspf_output(tgen, "r1", "cspf-ipv4-delay.txt", "10.0.255.1", "10.0.255.4", "delay 50000")
+ compare_cspf_output(tgen, "r1", "cspf-ipv4-delay.txt", "10.0.255.1", "10.0.255.4", "delay 50000", "rsv 7 100000000")
+
+
+def test_step3():
+ "Step3: Test CSPF from r1 to r4 for IPv6 with various metric"
+
+ tgen = setup_testcase("Step2: CSPF(r1, r4, IPv6)")
+
+ compare_cspf_output(tgen, "r1", "cspf-ipv6-metric.txt", "2001:db8:1::1:1", "2001:db8::4", "metric 50")
+ compare_cspf_output(tgen, "r1", "cspf-ipv6-te-metric.txt", "2001:db8::1", "2001:db8:5::3:4", "te-metric 80")
+ compare_cspf_output(tgen, "r1", "cspf-ipv6-delay.txt", "2001:db8::1", "2001:db8::4", "delay 80000")
+ compare_cspf_output(tgen, "r1", "cspf-ipv6-delay.txt", "2001:db8::1", "2001:db8::4", "delay 80000", "rsv 7 100000000")
+
+
+def test_step4():
+ "Step4: Test CSPF from r1 to r4 with no possible path"
+
+ tgen = setup_testcase("Step2: CSPF(r1, r4, failure)")
+
+ compare_cspf_output(tgen, "r1", "cspf-failed.txt", "10.0.255.1", "10.0.255.4", "metric 10")
+ compare_cspf_output(tgen, "r1", "cspf-failed.txt", "2001:db8::1", "2001:db8::4", "te-metric 50")
+ compare_cspf_output(tgen, "r1", "cspf-failed.txt", "10.0.255.1", "10.0.255.4", "delay 5000")
+ compare_cspf_output(tgen, "r1", "cspf-failed.txt", "2001:db8::1", "2001:db8::4", "delay 80000", "rsv 7 1000000000")
+ compare_cspf_output(tgen, "r1", "cspf-failed-src.txt", "10.0.0.3", "10.0.255.4", "metric 10")
+ compare_cspf_output(tgen, "r1", "cspf-failed-dst.txt", "10.0.0.1", "10.0.4.40", "metric 10")
+ compare_cspf_output(tgen, "r1", "cspf-failed-same.txt", "10.0.0.1", "10.0.0.1", "metric 10")
+
+
+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/docker/README.md b/tests/topotests/docker/README.md
new file mode 100644
index 0000000..2b40994
--- /dev/null
+++ b/tests/topotests/docker/README.md
@@ -0,0 +1,72 @@
+# Topotests in Docker
+
+## Quickstart
+
+If you have Docker installed, you can run the topotests in Docker.
+The easiest way to do this, is to use the make targets from this
+repository.
+
+Your current user needs to have access to the Docker daemon. Alternatively
+you can run these commands as root.
+
+```console
+make topotests
+```
+
+This command will pull the most recent topotests image from dockerhub, compile FRR inside
+of it, and run the topotests.
+
+## Advanced Usage
+
+Internally, the topotests make target uses a shell script to pull the image and spawn the docker
+container.
+
+There are several environment variables which can be used to modify the behavior
+of the script, these can be listed by calling it with `-h`:
+
+```console
+./tests/topotests/docker/frr-topotests.sh -h
+```
+
+For example, a volume is used to cache build artifacts between multiple runs
+of the image. If you need to force a complete recompile, you can set `TOPOTEST_CLEAN`:
+
+```console
+TOPOTEST_CLEAN=1 ./tests/topotests/docker/frr-topotests.sh
+```
+
+By default, `frr-topotests.sh` will build frr and run pytest. If you append
+arguments and the first one starts with `/` or `./`, they will replace the call to
+pytest. If the appended arguments do not match this patttern, they will be provided to
+pytest as arguments.
+
+So, to run a specific test with more verbose logging:
+
+```console
+./tests/topotests/docker/frr-topotests.sh -vv -s all-protocol-startup/test_all_protocol_startup.py
+```
+
+And to compile FRR but drop into a shell instead of running pytest:
+
+```console
+./tests/topotests/docker/frr-topotests.sh /bin/bash
+```
+
+## Development
+
+The docker image just includes all the components to run the topotests, but not the topotests
+themselves. So if you just want to write tests and don't want to make changes to the environment
+provided by the docker image. You don't need to build your own docker image if you do not want to.
+
+When developing new tests, there is one caveat though: The startup script of the container will
+run a `git-clean` on its copy of the FRR tree to avoid any pollution of the container with build
+artefacts from the host. This will also result in your newly written tests being unavailable in the
+container unless at least added to the index with `git-add`.
+
+If you do want to test changes to the docker image, you can locally build the image and run the tests
+without pulling from the registry using the following commands:
+
+```console
+make topotests-build
+TOPOTEST_PULL=0 make topotests
+```
diff --git a/tests/topotests/docker/build.sh b/tests/topotests/docker/build.sh
new file mode 100755
index 0000000..70ce2ef
--- /dev/null
+++ b/tests/topotests/docker/build.sh
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+cd "$(dirname "$0")"/..
+
+exec docker build --pull \
+ --compress \
+ -t frrouting/topotests:latest \
+ .
diff --git a/tests/topotests/docker/frr-topotests.sh b/tests/topotests/docker/frr-topotests.sh
new file mode 100755
index 0000000..1eaaea2
--- /dev/null
+++ b/tests/topotests/docker/frr-topotests.sh
@@ -0,0 +1,174 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+set -e
+
+if [[ "$1" = "-h" ]] || [[ "$1" = "--help" ]]; then
+ cat >&2 <<-EOF
+
+ This script runs the FRRouting topotests on the FRR tree
+ in the current working directory.
+
+ Usage: $0 [args...]
+
+ If any arguments are provided and the first argument starts with / or ./
+ the arguments are interpreted as command and will be executed instead
+ of pytest.
+
+ Behavior can be further modified by the following environment variables:
+
+ TOPOTEST_AUTOLOAD If set to 1, the script will try to load necessary
+ kernel modules without asking for confirmation first.
+
+ TOPOTEST_NOLOAD If set to 1, don't try to load necessary kernel
+ modules and don't even ask.
+
+ TOPOTEST_BUILDCACHE Docker volume used for caching multiple FRR builds
+ over container runs. By default a
+ \`topotest-buildcache\` volume will be created for
+ that purpose.
+
+ TOPOTEST_CLEAN Clean all previous build artifacts prior to
+ building. Disabled by default, set to 1 to enable.
+
+ TOPOTEST_DOC Build the documentation associated with FRR.
+ Disabled by default, set to 1 to enable.
+
+ TOPOTEST_FRR If set, don't test the FRR in the current working
+ directory, but the one at the given path.
+
+ TOPOTEST_LOGS If set, don't use \`/tmp/topotest_logs\` directory
+ but use the provided path instead.
+
+ TOPOTEST_OPTIONS These options are appended to the docker-run
+ command for starting the tests.
+
+ TOPOTEST_PULL If set to 0, don't try to pull the most recent
+ version of the docker image from dockerhub.
+
+ TOPOTEST_SANITIZER Controls whether to use the address sanitizer.
+ Enabled by default, set to 0 to disable.
+
+ TOPOTEST_VERBOSE Show detailed build output.
+ Enabled by default, set to 0 to disable.
+
+ EOF
+ exit 1
+fi
+
+#
+# These two modules are needed to run the MPLS tests.
+# They are often not automatically loaded.
+#
+# We cannot load them from the container since we don't
+# have host kernel modules available there. If we load
+# them from the host however, they can be used just fine.
+#
+
+export PATH="$PATH:/sbin:/usr/sbin:/usr/local/sbin"
+
+if [ "$TOPOTEST_NOLOAD" != "1" ]; then
+ for module in mpls-router mpls-iptunnel; do
+ if modprobe -n $module 2> /dev/null; then
+ :
+ else
+ # If the module doesn't exist, we cannot do anything about it
+ continue
+ fi
+
+ if [ $(grep -c ${module/-/_} /proc/modules) -ne 0 ]; then
+ # If the module is loaded, we don't have to do anything
+ continue
+ fi
+
+ if [ "$TOPOTEST_AUTOLOAD" != "1" ]; then
+ echo "To run all the possible tests, we need to load $module."
+ echo -n "Do you want to proceed? [y/n] "
+ read answer
+ if [ x"$answer" != x"y" ]; then
+ echo "Not loading."
+ continue
+ fi
+ fi
+
+ if [ x"$(whoami)" = x"root" ]; then
+ modprobe $module
+ else
+ sudo modprobe $module
+ fi
+ done
+fi
+
+if [ -z "$TOPOTEST_LOGS" ]; then
+ mkdir -p /tmp/topotest_logs
+ TOPOTEST_LOGS="/tmp/topotest_logs"
+fi
+
+if [ -z "$TOPOTEST_FRR" ]; then
+ TOPOTEST_FRR="$(git rev-parse --show-toplevel || true)"
+ if [ -z "$TOPOTEST_FRR" ]; then
+ echo "Could not determine base of FRR tree." >&2
+ echo "frr-topotests only works if you have your tree in git." >&2
+ exit 1
+ fi
+ git -C "$TOPOTEST_FRR" ls-files -z > "${TOPOTEST_LOGS}/git-ls-files"
+fi
+
+if [ -z "$TOPOTEST_BUILDCACHE" ]; then
+ TOPOTEST_BUILDCACHE=topotest-buildcache
+ docker volume inspect "${TOPOTEST_BUILDCACHE}" &> /dev/null \
+ || docker volume create "${TOPOTEST_BUILDCACHE}"
+fi
+
+if [ "${TOPOTEST_PULL:-1}" = "1" ]; then
+ docker pull frrouting/topotests:latest
+fi
+
+if [[ -n "$TMUX" ]]; then
+ TMUX_OPTIONS="-v $(dirname $TMUX):$(dirname $TMUX) -e TMUX=$TMUX -e TMUX_PANE=$TMUX_PANE"
+fi
+
+if [[ -n "$STY" ]]; then
+ SCREEN_OPTIONS="-v /run/screen:/run/screen -e STY=$STY"
+fi
+set -- --rm -i \
+ -v "$HOME:$HOME:ro" \
+ -v "$TOPOTEST_LOGS:/tmp" \
+ -v "$TOPOTEST_FRR:/root/host-frr:ro" \
+ -v "$TOPOTEST_BUILDCACHE:/root/persist" \
+ -e "TOPOTEST_CLEAN=$TOPOTEST_CLEAN" \
+ -e "TOPOTEST_VERBOSE=$TOPOTEST_VERBOSE" \
+ -e "TOPOTEST_DOC=$TOPOTEST_DOC" \
+ -e "TOPOTEST_SANITIZER=$TOPOTEST_SANITIZER" \
+ --privileged \
+ $SCREEN_OPTINS \
+ $TMUX_OPTIONS \
+ $TOPOTEST_OPTIONS \
+ frrouting/topotests:latest "$@"
+
+if [ -t 0 ]; then
+ set -- -t "$@"
+fi
+
+exec docker run "$@"
diff --git a/tests/topotests/docker/inner/compile_frr.sh b/tests/topotests/docker/inner/compile_frr.sh
new file mode 100755
index 0000000..495beaf
--- /dev/null
+++ b/tests/topotests/docker/inner/compile_frr.sh
@@ -0,0 +1,106 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+set -e
+
+# Load shared functions
+CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. $CDIR/funcs.sh
+
+#
+# Script begin
+#
+
+if [ "${TOPOTEST_CLEAN}" != "0" ]; then
+ log_info "Cleaning FRR builddir..."
+ rm -rf $FRR_BUILD_DIR &> /dev/null
+fi
+
+log_info "Syncing FRR source with host..."
+mkdir -p $FRR_BUILD_DIR
+rsync -a --info=progress2 \
+ --from0 --files-from=/tmp/git-ls-files \
+ --chown root:root \
+ $FRR_HOST_DIR/. $FRR_BUILD_DIR/
+
+cd "$FRR_BUILD_DIR" || \
+ log_fatal "failed to find frr directory"
+
+if [ "${TOPOTEST_VERBOSE}" != "0" ]; then
+ exec 3>&1
+else
+ exec 3>/dev/null
+fi
+
+log_info "Building FRR..."
+
+if [ ! -e configure ]; then
+ bash bootstrap.sh >&3 || \
+ log_fatal "failed to bootstrap configuration"
+fi
+
+if [ "${TOPOTEST_DOC}" != "0" ]; then
+ EXTRA_CONFIGURE+=" --enable-doc "
+else
+ EXTRA_CONFIGURE+=" --disable-doc "
+fi
+
+if [ ! -e Makefile ]; then
+ if [ "${TOPOTEST_SANITIZER}" != "0" ]; then
+ export CC="gcc"
+ export CFLAGS="-O1 -g -fsanitize=address -fno-omit-frame-pointer"
+ export LDFLAGS="-g -fsanitize=address -ldl"
+ touch .address_sanitizer
+ else
+ rm -f .address_sanitizer
+ fi
+
+ bash configure >&3 \
+ --enable-static-bin \
+ --enable-static \
+ --enable-shared \
+ --enable-dev-build \
+ --with-moduledir=/usr/lib/frr/modules \
+ --prefix=/usr \
+ --localstatedir=/var/run/frr \
+ --sbindir=/usr/lib/frr \
+ --sysconfdir=/etc/frr \
+ --enable-multipath=0 \
+ --enable-fpm \
+ --enable-sharpd \
+ $EXTRA_CONFIGURE \
+ --with-pkg-extra-version=-topotests \
+ || log_fatal "failed to configure the sources"
+fi
+
+# if '.address_sanitizer' file exists it means we are using address sanitizer.
+if [ -f .address_sanitizer ]; then
+ make -C lib CFLAGS="-g -O2" LDFLAGS="-g" clippy >&3
+fi
+
+make -j$(cpu_count) >&3 || \
+ log_fatal "failed to build the sources"
+
+make install >/dev/null || \
+ log_fatal "failed to install frr"
diff --git a/tests/topotests/docker/inner/entrypoint.sh b/tests/topotests/docker/inner/entrypoint.sh
new file mode 100755
index 0000000..451d0a2
--- /dev/null
+++ b/tests/topotests/docker/inner/entrypoint.sh
@@ -0,0 +1,50 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Load shared functions
+CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. $CDIR/funcs.sh
+
+set -e
+
+#
+# Script begin
+#
+"${CDIR}/compile_frr.sh"
+"${CDIR}/openvswitch.sh"
+
+cd "${FRR_BUILD_DIR}/tests/topotests"
+
+log_info "Setting permissions on /tmp so we can generate logs"
+chmod 1777 /tmp
+
+if [ $# -eq 0 ] || ([[ "$1" != /* ]] && [[ "$1" != ./* ]]); then
+ export TOPOTESTS_CHECK_MEMLEAK=/tmp/memleak_
+ export TOPOTESTS_CHECK_STDERR=Yes
+ set -- pytest \
+ --junitxml /tmp/topotests.xml \
+ "$@"
+fi
+
+exec "$@"
diff --git a/tests/topotests/docker/inner/funcs.sh b/tests/topotests/docker/inner/funcs.sh
new file mode 100755
index 0000000..d78d500
--- /dev/null
+++ b/tests/topotests/docker/inner/funcs.sh
@@ -0,0 +1,72 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+FRR_HOST_DIR=/root/host-frr
+FRR_BUILD_DIR=/root/persist/frr-build
+
+if [ ! -L "/root/frr" ]; then
+ ln -s $FRR_BUILD_DIR /root/frr
+fi
+
+[ -z $TOPOTEST_CLEAN ] && TOPOTEST_CLEAN=0
+[ -z $TOPOTEST_VERBOSE ] && TOPOTEST_VERBOSE=1
+[ -z $TOPOTEST_DOC ] && TOPOTEST_DOC=0
+[ -z $TOPOTEST_SANITIZER ] && TOPOTEST_SANITIZER=1
+
+log_info() {
+ local msg=$1
+
+ echo -e "=> $msg"
+}
+
+log_error() {
+ local msg=$1
+
+ echo -e "E: $msg" 2>&1
+}
+
+log_warning() {
+ local msg=$1
+
+ echo -e "W: $msg" 2>&1
+}
+
+log_fatal() {
+ local msg=$1
+
+ echo -e "F: $msg" 2>&1
+
+ exit 1
+}
+
+cpu_count() {
+ local cpu_count
+
+ cpu_count=$(cat /proc/cpuinfo | grep -w processor | wc -l)
+ if [ $? -eq 0 ]; then
+ echo -n $cpu_count
+ else
+ echo -n 2
+ fi
+}
diff --git a/tests/topotests/docker/inner/motd.txt b/tests/topotests/docker/inner/motd.txt
new file mode 100644
index 0000000..1e2f34f
--- /dev/null
+++ b/tests/topotests/docker/inner/motd.txt
@@ -0,0 +1,15 @@
+Welcome to the topotests container.
+
+Here are some useful tips:
+* After changing the FRR/Topotests sources, you may rebuild them
+ using the command `compile_frr.sh`. The build command has the
+ following environment variables:
+ - TOPOTEST_CLEAN: whether we should distclean or not (disabled by default)
+ - TOPOTEST_VERBOSE: show build messages (enabled by default)
+ - TOPOTEST_DOC: whether we should build docs or not (disabled by default)
+ - TOPOTEST_SANITIZER: whether we should use the address sanitizer (enabled by default)
+
+ Usage example: env TOPOTEST_CLEAN=1 compile_frr.sh
+
+* The topotests log directory can be found on your host machine on
+ `/tmp/topotests_logs`.
diff --git a/tests/topotests/docker/inner/openvswitch.sh b/tests/topotests/docker/inner/openvswitch.sh
new file mode 100755
index 0000000..67142f0
--- /dev/null
+++ b/tests/topotests/docker/inner/openvswitch.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Copyright 2018 Network Device Education Foundation, Inc. ("NetDEF")
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# Load shared functions
+CDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+. $CDIR/funcs.sh
+
+#
+# Script begin
+#
+
+log_info "Configuring OpenvSwitch...."
+
+# Configure OpenvSwitch so we are able to run mininet
+mkdir -p /var/run/openvswitch
+ovsdb-tool create /etc/openvswitch/conf.db \
+ /usr/share/openvswitch/vswitch.ovsschema
+ovsdb-server /etc/openvswitch/conf.db \
+ --remote=punix:/var/run/openvswitch/db.sock \
+ --remote=ptcp:6640 --pidfile=ovsdb-server.pid >/dev/null 2>/dev/null & \
+ disown
+ovs-vswitchd >/dev/null 2>/dev/null & disown
+
+sleep 2
+
+ovs-vsctl --no-wait -- init
+ovs_version=$(ovs-vsctl -V | grep ovs-vsctl | awk '{print $4}')
+ovs_db_version=$(\
+ ovsdb-tool schema-version /usr/share/openvswitch/vswitch.ovsschema)
+ovs-vsctl --no-wait -- set Open_vSwitch . db-version="${ovs_db_version}"
+ovs-vsctl --no-wait -- set Open_vSwitch . ovs-version="${ovs_version}"
+ovs-vsctl --no-wait -- set Open_vSwitch . system-type="docker-ovs"
+ovs-vsctl --no-wait -- set Open_vSwitch . system-version="0.1"
+ovs-vsctl --no-wait -- \
+ set Open_vSwitch . external-ids:system-id=`cat /proc/sys/kernel/random/uuid`
+ovs-vsctl --no-wait -- set-manager ptcp:6640
+ovs-appctl -t ovsdb-server \
+ ovsdb-server/add-remote db:Open_vSwitch,Open_vSwitch,manager_options
diff --git a/tests/topotests/eigrp_topo1/r1/eigrpd.conf b/tests/topotests/eigrp_topo1/r1/eigrpd.conf
new file mode 100644
index 0000000..6c800ab
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r1/eigrpd.conf
@@ -0,0 +1,8 @@
+log file eigrpd.log
+!
+router eigrp 1
+ network 0.0.0.0/0
+!
+line vty
+!
+
diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json
new file mode 100644
index 0000000..be0fdcf
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.json
@@ -0,0 +1,32 @@
+{
+ "P": {
+ "192.168.1.0/24": {
+ "fd": "28160",
+ "interface": " r1-eth0",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ },
+ "192.168.3.0/24": {
+ "fd": "33280",
+ "interface": " r1-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.1.2 (33280/30720)"
+ },
+ "193.1.1.0/26": {
+ "fd": "28160",
+ "interface": " r1-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ },
+ "193.1.2.0/24": {
+ "fd": "30720",
+ "interface": " r1-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.1.2 (30720/28160)"
+ }
+ }
+}
diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref
new file mode 100644
index 0000000..a2d7b33
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r1/show_ip_eigrp.ref
@@ -0,0 +1,14 @@
+
+EIGRP Topology Table for AS(1)/ID(193.1.1.1)
+
+Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply
+ r - reply Status, s - sia Status
+
+P 193.1.1.0/26, 1 successors, FD is 28160, serno: 0
+ via Connected, r1-eth1
+P 192.168.1.0/24, 1 successors, FD is 28160, serno: 0
+ via Connected, r1-eth0
+P 193.1.2.0/24, 1 successors, FD is 30720, serno: 0
+ via 193.1.1.2 (30720/28160), r1-eth1
+P 192.168.3.0/24, 1 successors, FD is 33280, serno: 0
+ via 193.1.1.2 (33280/30720), r1-eth1
diff --git a/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref
new file mode 100644
index 0000000..26fa7ca
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r1/show_ip_route.json_ref
@@ -0,0 +1,90 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24":[
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":33280,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.1.0/26":[
+ {
+ "prefix":"193.1.1.0/26",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"193.1.1.0/26",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.2.0/24":[
+ {
+ "prefix":"193.1.2.0/24",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":30720,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/eigrp_topo1/r1/zebra.conf b/tests/topotests/eigrp_topo1/r1/zebra.conf
new file mode 100644
index 0000000..51579a7
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r1/zebra.conf
@@ -0,0 +1,20 @@
+log file zebra.log
+! debug zebra rib detail
+!
+hostname r1
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth1
+ description to sw2 - RIPv2 interface
+ ip address 193.1.1.1/26
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/eigrp_topo1/r2/eigrpd.conf b/tests/topotests/eigrp_topo1/r2/eigrpd.conf
new file mode 100644
index 0000000..56c747d
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r2/eigrpd.conf
@@ -0,0 +1,7 @@
+log file eigrpd.log
+!
+!
+router eigrp 1
+ network 193.1.1.0/26
+ network 193.1.2.0/24
+
diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json
new file mode 100644
index 0000000..ae9f441
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.json
@@ -0,0 +1,32 @@
+{
+ "P": {
+ "192.168.1.0/24": {
+ "fd": "30720",
+ "interface": " r2-eth0",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.1.1 (30720/28160)"
+ },
+ "192.168.3.0/24": {
+ "fd": "30720",
+ "interface": " r2-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.2.2 (30720/28160)"
+ },
+ "193.1.1.0/26": {
+ "fd": "28160",
+ "interface": " r2-eth0",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ },
+ "193.1.2.0/24": {
+ "fd": "28160",
+ "interface": " r2-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ }
+ }
+}
diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref
new file mode 100644
index 0000000..cce49cd
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r2/show_ip_eigrp.ref
@@ -0,0 +1,14 @@
+
+EIGRP Topology Table for AS(1)/ID(193.1.2.1)
+
+Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply
+ r - reply Status, s - sia Status
+
+P 193.1.1.0/26, 1 successors, FD is 28160, serno: 0
+ via Connected, r2-eth0
+P 192.168.1.0/24, 1 successors, FD is 30720, serno: 0
+ via 193.1.1.1 (30720/28160), r2-eth0
+P 193.1.2.0/24, 1 successors, FD is 28160, serno: 0
+ via Connected, r2-eth1
+P 192.168.3.0/24, 1 successors, FD is 30720, serno: 0
+ via 193.1.2.2 (30720/28160), r2-eth1
diff --git a/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref
new file mode 100644
index 0000000..71c931b
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r2/show_ip_route.json_ref
@@ -0,0 +1,90 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":30720,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24":[
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":30720,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.1.0/26":[
+ {
+ "prefix":"193.1.1.0/26",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"193.1.1.0/26",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.2.0/24":[
+ {
+ "prefix":"193.1.2.0/24",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"193.1.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/eigrp_topo1/r2/zebra.conf b/tests/topotests/eigrp_topo1/r2/zebra.conf
new file mode 100644
index 0000000..c440f3a
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r2/zebra.conf
@@ -0,0 +1,21 @@
+log file zebra.log
+!
+hostname r2
+!
+interface r2-eth0
+ description to sw2 - RIPv2 interface
+ ip address 193.1.1.2/26
+ no link-detect
+!
+interface r2-eth1
+ description to sw3 - RIPv1 interface
+ ip address 193.1.2.1/24
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/eigrp_topo1/r3/eigrpd.conf b/tests/topotests/eigrp_topo1/r3/eigrpd.conf
new file mode 100644
index 0000000..53ad1bb
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r3/eigrpd.conf
@@ -0,0 +1,6 @@
+log file eigrpd.log
+!
+!
+router eigrp 1
+ network 0.0.0.0/0
+
diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json
new file mode 100644
index 0000000..83db66c
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.json
@@ -0,0 +1,32 @@
+{
+ "P": {
+ "192.168.1.0/24": {
+ "fd": "33280",
+ "interface": " r3-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.2.1 (33280/30720)"
+ },
+ "192.168.3.0/24": {
+ "fd": "28160",
+ "interface": " r3-eth0",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ },
+ "193.1.1.0/26": {
+ "fd": "30720",
+ "interface": " r3-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "193.1.2.1 (30720/28160)"
+ },
+ "193.1.2.0/24": {
+ "fd": "28160",
+ "interface": " r3-eth1",
+ "serno": "0",
+ "successors": "1",
+ "via": "Connected"
+ }
+ }
+}
diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref
new file mode 100644
index 0000000..70f3d3f
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r3/show_ip_eigrp.ref
@@ -0,0 +1,14 @@
+
+EIGRP Topology Table for AS(1)/ID(193.1.2.2)
+
+Codes: P - Passive, A - Active, U - Update, Q - Query, R - Reply
+ r - reply Status, s - sia Status
+
+P 193.1.1.0/26, 1 successors, FD is 30720, serno: 0
+ via 193.1.2.1 (30720/28160), r3-eth1
+P 192.168.1.0/24, 1 successors, FD is 33280, serno: 0
+ via 193.1.2.1 (33280/30720), r3-eth1
+P 193.1.2.0/24, 1 successors, FD is 28160, serno: 0
+ via Connected, r3-eth1
+P 192.168.3.0/24, 1 successors, FD is 28160, serno: 0
+ via Connected, r3-eth0
diff --git a/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref b/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref
new file mode 100644
index 0000000..5e0b79d
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r3/show_ip_route.json_ref
@@ -0,0 +1,108 @@
+{
+ "192.168.1.0/24":[
+ {
+ "prefix":"192.168.1.0/24",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":33280,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.2.0/24":[
+ {
+ "prefix":"192.168.2.0/24",
+ "protocol":"static",
+ "selected":true,
+ "distance":1,
+ "metric":0,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"192.168.3.10",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.3.0/24":[
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"192.168.3.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.1.0/26":[
+ {
+ "prefix":"193.1.1.0/26",
+ "protocol":"eigrp",
+ "selected":true,
+ "metric":30720,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"193.1.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "193.1.2.0/24":[
+ {
+ "prefix":"193.1.2.0/24",
+ "protocol":"eigrp",
+ "metric":28160,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"193.1.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/eigrp_topo1/r3/zebra.conf b/tests/topotests/eigrp_topo1/r3/zebra.conf
new file mode 100644
index 0000000..7f145b4
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface r3-eth0
+ description to sw4 - Stub interface
+ ip address 192.168.3.1/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw3 - RIPv2 interface
+ ip address 193.1.2.2/24
+ no link-detect
+!
+ip route 192.168.2.0/24 192.168.3.10
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot b/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot
new file mode 100644
index 0000000..ca3a0fe
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/test_eigrp_topo1.dot
@@ -0,0 +1,62 @@
+## GraphViz file for test_eigrp_topo1
+##
+## Color coding:
+#########################
+## Main FRR: #f08080 red
+## Switches: #d0e0d0 gray
+## RIP: #19e3d9 Cyan
+## RIPng: #fcb314 dark yellow
+## EIGRP: #696969
+## 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 test_eigrp_topo1 {
+ overlap=false;
+ constraint=false;
+
+ // title
+ labelloc="t";
+ label="Test Topologoy EIGRP Topo1";
+
+ ######################
+ # Routers
+ ######################
+
+ # Main FRR Router with all protocols
+ R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled];
+
+ # EIGRP Routers
+ R2 [shape=doubleoctagon, label="R2 FRR\nEIGRP Router", fillcolor="#19e3d9", style=filled];
+ R3 [shape=doubleoctagon, label="R3 FRR\nEIGRP Router", fillcolor="#19e3d9", style=filled];
+
+ ######################
+ # Network Lists
+ ######################
+
+ SW1_R1_stub [label="SW1\n192.168.1.0/24", fillcolor="#d0e0d0", style=filled];
+
+ # EIGRP Networks
+ SW2_R1_R2 [label="SW2\nEIGRPv2\n193.1.1.0/26", fillcolor="#d0e0d0", style=filled];
+ SW3_R2_R3 [label="SW3\nEIGRPv1\n193.1.2.0/24", fillcolor="#d0e0d0", style=filled];
+ SW4_R3 [label="SW4\n192.168.3.0/24", fillcolor="#d0e0d0", style=filled];
+ Net_R3_remote [label="Static Net\n192.168.2.0/24"];
+
+ ######################
+ # Network Connections
+ ######################
+ R1 -- SW1_R1_stub [label = "eth0\n.1\n::1"];
+
+ # EIGRP Network
+ R1 -- SW2_R1_R2 [label = "eth1\n.1"];
+ SW2_R1_R2 -- R2 [label = "eth0\n.2"];
+ R2 -- SW3_R2_R3 [label = "eth1\n.1"];
+ SW3_R2_R3 -- R3 [label = "eth1\n.2"];
+ R3 -- SW4_R3 [label = "eth0\n.1"];
+ SW4_R3 -- Net_R3_remote [label = ".10"];
+
+}
diff --git a/tests/topotests/eigrp_topo1/test_eigrp_topo1.py b/tests/topotests/eigrp_topo1/test_eigrp_topo1.py
new file mode 100644
index 0000000..8b7c9fc
--- /dev/null
+++ b/tests/topotests/eigrp_topo1/test_eigrp_topo1.py
@@ -0,0 +1,292 @@
+#!/usr/bin/env python
+
+#
+# test_eigrp_topo1.py
+#
+# Copyright (c) 2017 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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_eigrp_topo1.py: Testing EIGRP
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+
+pytestmark = [pytest.mark.eigrpd]
+
+# 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.
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Switches for EIGRP
+ # switch 2 switch is for connection to EIGRP router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # switch 4 is stub on remote EIGRP router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"])
+
+ # switch 3 is between EIGRP routers
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_EIGRP, os.path.join(CWD, "{}/eigrpd.conf".format(rname))
+ )
+
+ 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_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ topotest.sleep(5, "Waiting for EIGRP convergence")
+
+
+def test_eigrp_routes():
+ "Test EIGRP 'show ip eigrp'"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Verify EIGRP Status
+ logger.info("Verifying EIGRP routes")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ refTableFile = "{}/{}/show_ip_eigrp.json".format(CWD, router.name)
+
+ # Read expected result from file
+ expected = json.loads(open(refTableFile).read())
+
+ # Actual output from router
+ actual = ip_eigrp_topo(router)
+
+ assertmsg = '"show ip eigrp topo" mismatches on {}'.format(router.name)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+def test_zebra_ipv4_routingTable():
+ "Test 'show ip route'"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ failures = 0
+ router_list = tgen.routers().values()
+ for router in router_list:
+ output = router.vtysh_cmd("show ip route json", isjson=True)
+ refTableFile = "{}/{}/show_ip_route.json_ref".format(CWD, router.name)
+ expected = json.loads(open(refTableFile).read())
+
+ assertmsg = "Zebra IPv4 Routing Table verification failed for router {}".format(
+ router.name
+ )
+ assert topotest.json_cmp(output, expected) is None, assertmsg
+
+
+def test_shut_interface_and_recover():
+ "Test shutdown of an interface and recovery of the interface"
+
+ tgen = get_topogen()
+ router = tgen.gears["r1"]
+ router.run("ip link set r1-eth1 down")
+ topotest.sleep(5, "Waiting for EIGRP convergence")
+ router.run("ip link set r1-eth1 up")
+
+
+def test_shutdown_check_stderr():
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ pytest.skip("Skipping test for Stderr output and memory leaks")
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Verifying unexpected STDERR output from daemons")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ router.stop()
+
+ log = tgen.net[router.name].getStdErr("eigrpd")
+ if log:
+ logger.error("EIGRPd StdErr Log:" + log)
+ log = tgen.net[router.name].getStdErr("zebra")
+ if log:
+ logger.error("Zebra StdErr Log:" + log)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
+
+#
+# Auxiliary Functions
+#
+def ip_eigrp_topo(node):
+ """
+ Parse 'show ip eigrp topo' from `node` and returns a dict with the
+ result.
+
+ Example:
+ {
+ 'P': {
+ '192.168.1.0/24': {
+ 'sucessors': 1,
+ 'fd': 112233,
+ 'serno': 0,
+ 'via': 'Connected',
+ 'interface': 'eth0',
+ },
+ '192.168.2.0/24': {
+ 'sucessors': 1,
+ 'fd': 112234,
+ 'serno': 0,
+ 'via': 'Connected',
+ 'interface': 'eth1',
+ }
+ }
+ }
+ """
+ output = topotest.normalize_text(node.vtysh_cmd("show ip eigrp topo")).splitlines()
+ result = {}
+ for idx, line in enumerate(output):
+ columns = line.split(" ", 1)
+
+ # Parse the following format into python dicts
+ # code A.B.C.D/E, X successors, FD is Y, serno: Z
+ # via FOO, interface-name
+ code = columns[0]
+ if code not in ["P", "A", "U", "Q", "R", "r", "s"]:
+ continue
+
+ if code not in result:
+ result[code] = {}
+
+ # Split network from the rest
+ columns = columns[1].split(",")
+
+ # Parse first line data
+ network = columns[0]
+ result[code][network] = {}
+ for column in columns:
+ # Skip the network column
+ if column == columns[0]:
+ continue
+
+ match = re.search(r"(\d+) successors", column)
+ if match is not None:
+ result[code][network]["successors"] = match.group(1)
+ continue
+
+ match = re.search(r"FD is (\d+)", column)
+ if match is not None:
+ result[code][network]["fd"] = match.group(1)
+ continue
+
+ match = re.search(r"serno: (\d+)", column)
+ if match is not None:
+ result[code][network]["serno"] = match.group(1)
+ continue
+
+ # Parse second line data
+ nextline = output[idx + 1]
+ columns = topotest.normalize_text(nextline).split(",")
+ for column in columns:
+ match = re.search(r"via (.+)", column)
+ if match is not None:
+ result[code][network]["via"] = match.group(1)
+ continue
+
+ match = re.search(r"(.+)", column)
+ if match is not None:
+ result[code][network]["interface"] = match.group(1)
+ continue
+
+ return result
diff --git a/tests/topotests/evpn_pim_1/host1/bgpd.conf b/tests/topotests/evpn_pim_1/host1/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host1/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/evpn_pim_1/host1/pimd.conf b/tests/topotests/evpn_pim_1/host1/pimd.conf
new file mode 100644
index 0000000..63a44c1
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host1/pimd.conf
@@ -0,0 +1,4 @@
+int lo
+!
+
+
diff --git a/tests/topotests/evpn_pim_1/host1/zebra.conf b/tests/topotests/evpn_pim_1/host1/zebra.conf
new file mode 100644
index 0000000..45ad031
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host1/zebra.conf
@@ -0,0 +1,5 @@
+int host1-eth0
+ ip addr 192.168.3.4/24
+
+int lo
+ ip addr 192.168.100.4/32
diff --git a/tests/topotests/evpn_pim_1/host2/bgpd.conf b/tests/topotests/evpn_pim_1/host2/bgpd.conf
new file mode 100644
index 0000000..cdf4cb4
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host2/bgpd.conf
@@ -0,0 +1 @@
+!
diff --git a/tests/topotests/evpn_pim_1/host2/pimd.conf b/tests/topotests/evpn_pim_1/host2/pimd.conf
new file mode 100644
index 0000000..63a44c1
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host2/pimd.conf
@@ -0,0 +1,4 @@
+int lo
+!
+
+
diff --git a/tests/topotests/evpn_pim_1/host2/zebra.conf b/tests/topotests/evpn_pim_1/host2/zebra.conf
new file mode 100644
index 0000000..bfae530
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/host2/zebra.conf
@@ -0,0 +1,5 @@
+int host-eth0
+ ip addr 192.168.4.5/24
+
+int lo
+ ip addr 192.168.100.5/32
diff --git a/tests/topotests/evpn_pim_1/leaf1/bgpd.conf b/tests/topotests/evpn_pim_1/leaf1/bgpd.conf
new file mode 100644
index 0000000..97fd866
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf1/bgpd.conf
@@ -0,0 +1,11 @@
+
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ neighbor 192.168.1.1 timers 3 10
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.1 activate
+ advertise-all-vni
+ !
+!
diff --git a/tests/topotests/evpn_pim_1/leaf1/pimd.conf b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
new file mode 100644
index 0000000..b54aada
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf1/pimd.conf
@@ -0,0 +1,16 @@
+! debug pim events
+! debug pim nht
+! debug pim zebra
+ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
+!
+int lo
+ ip pim
+!
+int leaf1-eth0
+ ip pim
+!
+int leaf1-eth1
+ ip pim
+ ip igmp
+
diff --git a/tests/topotests/evpn_pim_1/leaf1/zebra.conf b/tests/topotests/evpn_pim_1/leaf1/zebra.conf
new file mode 100644
index 0000000..581cc6e
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf1/zebra.conf
@@ -0,0 +1,6 @@
+int leaf1-eth0
+ ip addr 192.168.1.2/24
+int leaf1-eth1
+ ip addr 192.168.3.2/24
+int lo
+ ip addr 192.168.100.2/32
diff --git a/tests/topotests/evpn_pim_1/leaf2/bgpd.conf b/tests/topotests/evpn_pim_1/leaf2/bgpd.conf
new file mode 100644
index 0000000..91d9bd8
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf2/bgpd.conf
@@ -0,0 +1,11 @@
+
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as external
+ neighbor 192.168.2.1 timers 3 10
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.2.1 activate
+ advertise-all-vni
+ !
+!
diff --git a/tests/topotests/evpn_pim_1/leaf2/pimd.conf b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
new file mode 100644
index 0000000..d775b80
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf2/pimd.conf
@@ -0,0 +1,14 @@
+ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
+!
+int lo
+ ip pim
+!
+int leaf2-eth0
+ ip pim
+!
+int leaf2-eth1
+ ip pim
+ ip igmp
+!
+
diff --git a/tests/topotests/evpn_pim_1/leaf2/zebra.conf b/tests/topotests/evpn_pim_1/leaf2/zebra.conf
new file mode 100644
index 0000000..1bcf8e1
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/leaf2/zebra.conf
@@ -0,0 +1,6 @@
+int leaf2-eth0
+ ip addr 192.168.2.3/24
+int leaf2-eth1
+ ip addr 192.168.4.3/24
+int lo
+ ip addr 192.168.100.3/32
diff --git a/tests/topotests/evpn_pim_1/spine/bgp.summ.json b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
new file mode 100644
index 0000000..5ff4b09
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/bgp.summ.json
@@ -0,0 +1,41 @@
+{
+ "routerId":"192.168.100.1",
+ "as":65001,
+ "vrfName":"default",
+ "tableVersion":7,
+ "peerCount":2,
+ "peers":{
+ "192.168.1.2":{
+ "remoteAs":65002,
+ "version":4,
+ "tableVersion":0,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":3,
+ "pfxSnt":7,
+ "state":"Established",
+ "connectionsEstablished":1,
+ "connectionsDropped":0,
+ "idType":"ipv4"
+ },
+ "192.168.2.3":{
+ "remoteAs":65003,
+ "version":4,
+ "tableVersion":0,
+ "outq":0,
+ "inq":0,
+ "pfxRcd":3,
+ "pfxSnt":7,
+ "state":"Established",
+ "connectionsEstablished":1,
+ "connectionsDropped":0,
+ "idType":"ipv4"
+ }
+ },
+ "failedPeers":0,
+ "totalPeers":2,
+ "dynamicPeers":0,
+ "bestPath":{
+ "multiPathRelax":"false"
+ }
+}
diff --git a/tests/topotests/evpn_pim_1/spine/bgpd.conf b/tests/topotests/evpn_pim_1/spine/bgpd.conf
new file mode 100644
index 0000000..81ab802
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/bgpd.conf
@@ -0,0 +1,13 @@
+
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+ neighbor 192.168.1.2 timers 3 10
+ neighbor 192.168.2.3 remote-as external
+ neighbor 192.168.2.3 timers 3 10
+ redistribute connected
+ address-family l2vpn evpn
+ neighbor 192.168.1.2 activate
+ neighbor 192.168.2.3 activate
+ exit-address-family
+!
diff --git a/tests/topotests/evpn_pim_1/spine/join-info.json b/tests/topotests/evpn_pim_1/spine/join-info.json
new file mode 100644
index 0000000..3d135fb
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/join-info.json
@@ -0,0 +1,34 @@
+{
+ "spine-eth0":{
+ "name":"spine-eth0",
+ "state":"up",
+ "address":"192.168.1.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.1.1.1":{
+ "*":{
+ "source":"*",
+ "group":"239.1.1.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN"
+ }
+ }
+ },
+ "spine-eth1":{
+ "name":"spine-eth1",
+ "state":"up",
+ "address":"192.168.2.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.1.1.1":{
+ "*":{
+ "source":"*",
+ "group":"239.1.1.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN"
+ }
+ }
+ }
+}
diff --git a/tests/topotests/evpn_pim_1/spine/pimd.conf b/tests/topotests/evpn_pim_1/spine/pimd.conf
new file mode 100644
index 0000000..12c6d6f
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/pimd.conf
@@ -0,0 +1,14 @@
+ip pim rp 192.168.100.1
+ip pim join-prune-interval 5
+!
+int lo
+ ip pim
+!
+int spine-eth0
+ ip pim
+!
+int spine-eth1
+ ip pim
+!
+
+
diff --git a/tests/topotests/evpn_pim_1/spine/zebra.conf b/tests/topotests/evpn_pim_1/spine/zebra.conf
new file mode 100644
index 0000000..2cb7194
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/spine/zebra.conf
@@ -0,0 +1,8 @@
+int spine-eth0
+ ip addr 192.168.1.1/24
+!
+int spine-eth1
+ ip addr 192.168.2.1/24
+!
+int lo
+ ip addr 192.168.100.1/32
diff --git a/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
new file mode 100644
index 0000000..6d5c096
--- /dev/null
+++ b/tests/topotests/evpn_pim_1/test_evpn_pim_topo1.py
@@ -0,0 +1,217 @@
+#!/usr/bin/env python
+
+#
+# test_evpn-pim_topo1.py
+#
+# Copyright (c) 2017 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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_pim_topo1.py: Testing evpn-pim
+
+"""
+
+import os
+import sys
+import pytest
+import json
+from functools import partial
+
+pytestmark = [pytest.mark.pimd]
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.bgpd]
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ tgen.add_router("spine")
+ tgen.add_router("leaf1")
+ tgen.add_router("leaf2")
+ tgen.add_router("host1")
+ tgen.add_router("host2")
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ # spine-eth0 is connected to leaf1-eth0
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["spine"])
+ switch.add_link(tgen.gears["leaf1"])
+
+ # spine-eth1 is connected to leaf2-eth0
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["spine"])
+ switch.add_link(tgen.gears["leaf2"])
+
+ # leaf1-eth1 is connected to host1-eth0
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["leaf1"])
+ switch.add_link(tgen.gears["host1"])
+
+ # leaf2-eth1 is connected to host2-eth0
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["leaf2"])
+ switch.add_link(tgen.gears["host2"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ leaf1 = tgen.gears["leaf1"]
+ leaf2 = tgen.gears["leaf2"]
+
+ leaf1.run("brctl addbr brleaf1")
+ leaf2.run("brctl addbr brleaf2")
+ leaf1.run("ip link set dev brleaf1 up")
+ leaf2.run("ip link set dev brleaf2 up")
+ leaf1.run(
+ "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf1-eth1 dstport 4789"
+ )
+ leaf2.run(
+ "ip link add vxlan0 type vxlan id 42 group 239.1.1.1 dev leaf2-eth1 dstport 4789"
+ )
+ leaf1.run("brctl addif brleaf1 vxlan0")
+ leaf2.run("brctl addif brleaf2 vxlan0")
+ leaf1.run("ip link set up dev vxlan0")
+ leaf2.run("ip link set up dev vxlan0")
+ # tgen.mininet_cli()
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.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 test_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ spine = tgen.gears["spine"]
+ json_file = "{}/{}/bgp.summ.json".format(CWD, spine.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, spine, "show bgp ipv4 uni summ json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=125, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(spine.name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_multicast_groups_on_rp():
+ "Ensure the multicast groups show up on the spine"
+ # This test implicitly tests the auto mcast groups
+ # of the created vlans and then the auto-joins that
+ # pim will do to the RP( spine )
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ spine = tgen.gears["spine"]
+ json_file = "{}/{}/join-info.json".format(CWD, spine.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, spine, "show ip pim join json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" JSON output mismatches'.format(spine.name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_shutdown_check_stderr():
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ pytest.skip("Skipping test for Stderr output and memory leaks")
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Verifying unexpected STDERR output from daemons")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ router.stop()
+
+ log = tgen.net[router.name].getStdErr("pimd")
+ if log:
+ logger.error("PIMd StdErr Log:" + log)
+ log = tgen.net[router.name].getStdErr("bgpd")
+ if log:
+ logger.error("BGPd StdErr Log:" + log)
+ log = tgen.net[router.name].getStdErr("zebra")
+ if log:
+ logger.error("Zebra StdErr Log:" + log)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
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 0000000..e69de29
--- /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 0000000..14842da
--- /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 0000000..dd41270
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/evpn_type5_topo1.json
@@ -0,0 +1,1004 @@
+{
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "2",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"}
+ ],
+ "neighbor": {
+ "e1": {
+ "dest_link": {
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ],
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "e1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "e1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "deactivate": "ipv4"
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "e1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3,
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "100",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d1-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "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",
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "d2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "BLUE",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "l2vpn": {
+ "evpn": {
+ "advertise": {
+ "ipv4": {
+ "unicast": {}
+ },
+ "ipv6": {
+ "unicast": {}
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "200",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r4": {
+ "dest_link": {
+ "d2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "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": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "local_as": "4",
+ "vrf": "GREEN",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "d1": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ },
+ "d2": {
+ "dest_link": {
+ "r4-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 3
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+}
+
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 100644
index 0000000..332ffdf
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_chaos_topo1.py
@@ -0,0 +1,1021 @@
+#!/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 sys
+import time
+import pytest
+import platform
+from copy import deepcopy
+
+
+# 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 lib.common_config import (
+ start_topology,
+ write_test_header,
+ check_address_types,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ create_static_routes,
+ create_vrf_cfg,
+ check_router_status,
+ configure_vxlan,
+ configure_brctl,
+ verify_vrf_vni,
+ verify_cli_json,
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ verify_attributes_for_evpn_routes,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Reading the data from JSON File for topology creation
+# Global variables
+TCPDUMP_FILE = "evpn_log.txt"
+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],
+}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/evpn_type5_chaos_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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(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(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(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 100644
index 0000000..a641fec
--- /dev/null
+++ b/tests/topotests/evpn_type5_test_topo1/test_evpn_type5_topo1.py
@@ -0,0 +1,2316 @@
+#!/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 sys
+import json
+import time
+import pytest
+import platform
+from copy import deepcopy
+
+
+# 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 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,
+ create_static_routes,
+ create_vrf_cfg,
+ check_router_status,
+ apply_raw_config,
+ configure_vxlan,
+ configure_brctl,
+ create_interface_in_kernel,
+ kill_router_daemons,
+ start_router_daemons
+)
+
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_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
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+# 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],
+}
+
+
+def build_topo(tgen):
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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(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(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(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)
+ 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)
+ 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(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
+ )
+
+ result = verify_bgp_convergence(tgen, topo, dut)
+ assert result is True, "Failed to converge on {}".format(dut)
+
+ 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)
+
+
+def test_evpn_address_family_with_graceful_restart_p0(request):
+ """
+ Verify Graceful-restart function 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)
+
+ 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 DCG-2 as GR restarting node for EVPN session between"
+ " DCG-2 and EDGE-1, following by a session reset using 'clear bgp *'"
+ " command.")
+
+ input_dict_gr = {
+ "d2": {
+ "bgp":
+ [
+ {
+ "local_as": "200",
+ "graceful-restart": {
+ "graceful-restart": True,
+ }
+ }
+ ]
+ }
+ }
+
+ result = create_router_bgp(tgen, topo, input_dict_gr)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Verify that DCG-2 changes it's role to GR-restarting router "
+ "and EDGE-1 becomes the GR-helper.")
+
+ step("Kill BGPd daemon on DCG-2.")
+ kill_router_daemons(tgen, "d2", ["bgpd"])
+
+ step("Verify that EDGE-1 keep stale entries for EVPN RT-5 routes "
+ "received from DCG-2 before the restart.")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "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_evpn_routes(tgen, topo, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that DCG-2 keeps BGP routes in Zebra until BGPd "
+ "comes up or end of 'rib-stale-time'")
+
+ step("Start BGPd daemon on DCG-2.")
+ start_router_daemons(tgen, "d2", ["bgpd"])
+
+ step("Verify that EDGE-1 removed all the stale entries.")
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "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_evpn_routes(tgen, topo, "e1", input_routes)
+ assert result is True, "Testcase {} :Failed \n Error: {}". \
+ format(tc_name, result)
+
+ step("Verify that DCG-2 refresh zebra with EVPN routes. "
+ "(no significance of 'rib-stale-time'")
+
+ for addr_type in ADDR_TYPES:
+ input_routes = {
+ "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, "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": [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {
+ "as_num": "123 231 321",
+ "as_action": "prepend",
+ }
+ },
+ }
+ ],
+ "rmap_d2": [
+ {
+ "action": "permit",
+ "set": {
+ attribute: {"as_num": "121", "as_action": "prepend"}
+ },
+ }
+ ],
+ }
+ }
+ }
+ else:
+ input_dict_1 = {
+ "e1": {
+ "route_maps": {
+ "rmap_d1": [{"action": "permit", "set": {attribute: 120}}],
+ "rmap_d2": [{"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/example_test/__init__.py b/tests/topotests/example_test/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/example_test/__init__.py
diff --git a/tests/topotests/example_test/r1/zebra.conf b/tests/topotests/example_test/r1/zebra.conf
new file mode 100644
index 0000000..b733b7b
--- /dev/null
+++ b/tests/topotests/example_test/r1/zebra.conf
@@ -0,0 +1,8 @@
+interface r1-eth0
+ ip address 192.168.1.1/24
+
+interface r1-eth1
+ ip address 192.168.2.1/24
+
+interface r1-eth2
+ ip address 192.168.3.1/24 \ No newline at end of file
diff --git a/tests/topotests/example_test/r2/zebra.conf b/tests/topotests/example_test/r2/zebra.conf
new file mode 100644
index 0000000..c0921f5
--- /dev/null
+++ b/tests/topotests/example_test/r2/zebra.conf
@@ -0,0 +1,4 @@
+interface r2-eth0
+ ip address 192.168.1.2/24
+interface r2-eth1
+ ip address 192.168.3.2/24
diff --git a/tests/topotests/example_test/test_example.py b/tests/topotests/example_test/test_example.py
new file mode 100755
index 0000000..30c3d24
--- /dev/null
+++ b/tests/topotests/example_test/test_example.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+import subprocess
+import sys
+import os
+import time
+
+import pytest
+
+fatal_error = ""
+
+
+def setup_module(module):
+ print("setup_module module:%s" % module.__name__)
+
+
+def teardown_module(module):
+ print("teardown_module module:%s" % module.__name__)
+
+
+def setup_function(function):
+ print("setup_function function:%s" % function.__name__)
+
+
+def teardown_function(function):
+ print("teardown_function function:%s" % function.__name__)
+
+
+def test_numbers_compare():
+ a = 12
+ print("Dummy Output")
+ assert a == 12
+
+
+def test_fail_example():
+ assert True, "Some Text with explaination in case of failure"
+
+
+@pytest.mark.xfail
+def test_ls_exits_zero():
+ "Tests for ls command on invalid file"
+
+ global fatal_error
+
+ proc = subprocess.Popen(
+ ["ls", "/some/nonexistant/file"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ stdout, stderr = proc.communicate()
+
+ if proc.returncode != 0:
+ # Mark this as a fatal error which skips some other tests on failure
+ fatal_error = "test_fail_example failed"
+ assert proc.returncode == 0, "Return Code is non-Zero:\n%s" % stderr
+
+
+def test_skipped_on_fatalerror():
+ global fatal_error
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ assert True, "Some Text with explaination in case of failure"
+
+
+if __name__ == "__main__":
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/example_test/test_template.dot b/tests/topotests/example_test/test_template.dot
new file mode 100644
index 0000000..b5e1202
--- /dev/null
+++ b/tests/topotests/example_test/test_template.dot
@@ -0,0 +1,51 @@
+## 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="template";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n192.168.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s2\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="eth0\n.1"];
+
+ r1 -- s2 [label="eth1\n.100"];
+ r2 -- s2 [label="eth0\n.1"];
+}
diff --git a/tests/topotests/example_test/test_template.jpg b/tests/topotests/example_test/test_template.jpg
new file mode 100644
index 0000000..b01ef73
--- /dev/null
+++ b/tests/topotests/example_test/test_template.jpg
Binary files differ
diff --git a/tests/topotests/example_test/test_template.py b/tests/topotests/example_test/test_template.py
new file mode 100644
index 0000000..4c073f2
--- /dev/null
+++ b/tests/topotests/example_test/test_template.py
@@ -0,0 +1,173 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# <template>.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+<template>.py: Test <template>.
+"""
+
+import sys
+import pytest
+
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+# TODO: select markers based on daemons used during test
+# pytest module level markers
+pytestmark = [
+ # pytest.mark.babeld,
+ # pytest.mark.bfdd,
+ # pytest.mark.bgpd,
+ # pytest.mark.eigrpd,
+ # pytest.mark.isisd,
+ # pytest.mark.ldpd,
+ # pytest.mark.nhrpd,
+ # pytest.mark.ospf6d,
+ pytest.mark.ospfd,
+ # pytest.mark.pathd,
+ # pytest.mark.pbrd,
+ # pytest.mark.pimd,
+ # pytest.mark.ripd,
+ # pytest.mark.ripngd,
+ # pytest.mark.sharpd,
+ # pytest.mark.staticd,
+ # pytest.mark.vrrpd,
+]
+
+# Function we pass to Topogen to create the topology
+def build_topo(tgen):
+ "Build function"
+
+ # Create 2 routers
+ r1 = tgen.add_router("r1")
+ r2 = tgen.add_router("r2")
+
+ # Create a p2p connection between r1 and r2
+ tgen.add_link(r1, r2)
+
+ # Create a switch with one router connected to it to simulate a empty network.
+ switch = tgen.add_switch("s1")
+ switch.add_link(r1)
+
+ # Create a p2p connection between r1 and r2
+ switch = tgen.add_switch("s2")
+ switch.add_link(r1)
+ switch.add_link(r2)
+
+
+# New form of setup/teardown using pytest fixture
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, request.module.__name__)
+
+ # A basic topology similar to the above could also have be more easily specified
+ # using a # dictionary, remove the build_topo function and use the following
+ # instead:
+ #
+ # topodef = {
+ # "s1": "r1"
+ # "s2": ("r1", "r2")
+ # }
+ # tgen = Topogen(topodef, request.module.__name__)
+
+ # ... and here it calls initialization functions.
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all routers arrange for:
+ # - starting zebra using config file from <rtrname>/zebra.conf
+ # - starting ospfd using an empty config file.
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF)
+
+ # Start and configure the router daemons
+ tgen.start_router()
+
+ # Provide tgen as argument to each test function
+ yield tgen
+
+ # Teardown after last test runs
+ tgen.stop_topology()
+
+
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ===================
+# The tests functions
+# ===================
+
+
+def test_get_version(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ version = r1.vtysh_cmd("show version")
+ logger.info("FRR version is: " + version)
+
+
+def test_connectivity(tgen):
+ "Test the logs the FRR version"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+ output = r1.cmd_raises("ping -c1 192.168.1.2")
+ output = r2.cmd_raises("ping -c1 192.168.3.1")
+
+
+@pytest.mark.xfail
+def test_expect_failure(tgen):
+ "A test that is current expected to fail but should be fixed"
+
+ assert False, "Example of temporary expected failure that will eventually be fixed"
+
+
+@pytest.mark.skip
+def test_will_be_skipped(tgen):
+ "A test that will be skipped"
+ assert False
+
+
+# Memory leak test template
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ 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/example_test/test_template_json.json b/tests/topotests/example_test/test_template_json.json
new file mode 100644
index 0000000..1ed4a9d
--- /dev/null
+++ b/tests/topotests/example_test/test_template_json.json
@@ -0,0 +1,188 @@
+
+{
+ "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":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r1":{"ipv4":"auto", "ipv6":"auto"},
+ "r2":{"ipv4":"auto", "ipv6":"auto"},
+ "r4":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4":{
+ "links":{
+ "lo": {"ipv4": "auto", "ipv6": "auto", "type": "loopback"},
+ "r3":{"ipv4":"auto", "ipv6":"auto"}
+ },
+ "bgp":{
+ "local_as":"200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/example_test/test_template_json.py b/tests/topotests/example_test/test_template_json.py
new file mode 100644
index 0000000..42e8bc6
--- /dev/null
+++ b/tests/topotests/example_test/test_template_json.py
@@ -0,0 +1,68 @@
+#!/usr/bin/env python3
+#
+# September 5 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+# Copyright (c) 2017 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.
+#
+
+"""
+<template>.py: Test <template>.
+"""
+
+import pytest
+
+# Import topogen and topotest helpers
+from lib import bgp
+from lib import fixtures
+
+
+# TODO: select markers based on daemons used during test
+pytestmark = [
+ pytest.mark.bgpd,
+ # pytest.mark.ospfd,
+ # pytest.mark.ospf6d
+ # ...
+]
+
+# Use tgen_json fixture (invoked by use test arg of same name) to
+# setup/teardown standard JSON topotest
+tgen = pytest.fixture(fixtures.tgen_json, scope="module")
+
+
+# tgen is defined above
+# topo is a fixture defined in ../conftest.py
+def test_bgp_convergence(tgen, topo):
+ "Test for BGP convergence."
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ bgp_convergence = bgp.verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence
+
+
+# Memory leak test template
+def test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ if not tgen.is_memleak_enabled():
+ pytest.skip("Memory leak test/report is disabled")
+
+ tgen.report_memory_leaks()
diff --git a/tests/topotests/example_topojson_test/__init__.py b/tests/topotests/example_topojson_test/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/example_topojson_test/__init__.py
diff --git a/tests/topotests/example_topojson_test/test_topo_json_multiple_links/__init__.py b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/__init__.py
diff --git a/tests/topotests/example_topojson_test/test_topo_json_multiple_links/example_topojson_multiple_links.json b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/example_topojson_multiple_links.json
new file mode 100644
index 0000000..3968348
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/example_topojson_multiple_links.json
@@ -0,0 +1,152 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "100.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.1"
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "10.0.0.1/30",
+ "next_hop": "10.0.0.5"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/tests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py
new file mode 100755
index 0000000..fe4a256
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_multiple_links/test_example_topojson_multiple_links.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+<example>.py: Test <example tests>.
+"""
+
+import os
+import sys
+import json
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+
+# TODO: select markers based on daemons used during test
+# pytest module level markers
+"""
+pytestmark = pytest.mark.bfdd # single marker
+pytestmark = [
+ pytest.mark.bgpd,
+ pytest.mark.ospfd,
+ pytest.mark.ospf6d
+] # multiple markers
+"""
+
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/example_topojson_multiple_links.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
+bgp_convergence = False
+input_dict = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to create topology
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating 2 routers having 2 links in between,
+ # one is used to establised BGP neighborship
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # This function only purpose is to create configuration
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating configuration defined in input JSON
+ # file, example, BGP config, interface config, static routes
+ # config, prefix list config
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def test_bgp_convergence(request):
+ "Test BGP daemon convergence"
+
+ tgen = get_topogen()
+ global bgp_convergence
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert (
+ bgp_convergence is True
+ ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence)
+
+ logger.info("BGP is converged successfully \n")
+ write_test_footer(tc_name)
+
+
+def test_static_routes(request):
+ "Test to create and verify static routes."
+
+ tgen = get_topogen()
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Static routes are created as part of initial configuration,
+ # verifying RIB
+ dut = "r3"
+ protocol = "bgp"
+ next_hop = "10.0.0.1"
+ input_dict = {"r1": topo["routers"]["r1"]}
+
+ # Uncomment below to debug
+ # tgen.mininet_cli()
+ result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop)
+ 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/example_topojson_test/test_topo_json_single_link/__init__.py b/tests/topotests/example_topojson_test/test_topo_json_single_link/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link/__init__.py
diff --git a/tests/topotests/example_topojson_test/test_topo_json_single_link/example_topojson.json b/tests/topotests/example_topojson_test/test_topo_json_single_link/example_topojson.json
new file mode 100644
index 0000000..629d2d6
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link/example_topojson.json
@@ -0,0 +1,153 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "100.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.1"
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {}
+ }
+ },
+ "r1": {
+ "dest_link": {
+ "r3": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "10.0.0.1/30",
+ "next_hop": "10.0.0.5"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py b/tests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py
new file mode 100755
index 0000000..8bc2852
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link/test_example_topojson.py
@@ -0,0 +1,197 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+<example>.py: Test <example tests>.
+"""
+
+import os
+import sys
+import time
+import json
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+
+# TODO: select markers based on daemons used during test
+# pytest module level markers
+"""
+pytestmark = pytest.mark.bfdd # single marker
+pytestmark = [
+ pytest.mark.bgpd,
+ pytest.mark.ospfd,
+ pytest.mark.ospf6d
+] # multiple markers
+"""
+
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/example_topojson.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
+bgp_convergence = False
+input_dict = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to create topology
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating 2 routers having single links in between,
+ # which is used to establised BGP neighborship
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # This function only purpose is to create configuration
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating configuration defined in input JSON
+ # file, example, BGP config, interface config, static routes
+ # config, prefix list config
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def test_bgp_convergence(request):
+ "Test BGP daemon convergence"
+
+ tgen = get_topogen()
+ global bgp_convergence
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert (
+ bgp_convergence is True
+ ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence)
+
+ logger.info("BGP is converged successfully \n")
+ write_test_footer(tc_name)
+
+
+def test_static_routes(request):
+ "Test to create and verify static routes."
+
+ tgen = get_topogen()
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Static routes are created as part of initial configuration,
+ # verifying RIB
+ dut = "r3"
+ next_hop = "10.0.0.1"
+ input_dict = {"r1": topo["routers"]["r1"]}
+
+ # Uncomment below to debug
+ # tgen.mininet_cli()
+ result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop)
+ 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/example_topojson_test/test_topo_json_single_link_loopback/__init__.py b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/__init__.py
diff --git a/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/example_topojson.json b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/example_topojson.json
new file mode 100644
index 0000000..c76c626
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/example_topojson.json
@@ -0,0 +1,161 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "1.0.2.17/32",
+ "next_hop": "10.0.0.2"
+ }
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {
+ "redist_type": "static"
+ }
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "100.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.1"
+ },
+ {
+ "network": "1.0.1.17/32",
+ "next_hop": "10.0.0.1"
+ },
+ {
+ "network": "1.0.3.17/32",
+ "next_hop": "10.0.0.6"
+ }
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "lo": {
+ "source_link": "lo"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {
+ "network": "1.0.2.17/32",
+ "next_hop": "10.0.0.5"
+ },
+ {
+ "network": "10.0.0.1/30",
+ "next_hop": "10.0.0.5"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py
new file mode 100755
index 0000000..caf0a7c
--- /dev/null
+++ b/tests/topotests/example_topojson_test/test_topo_json_single_link_loopback/test_example_topojson.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+<example>.py: Test <example tests>.
+"""
+
+import os
+import sys
+import time
+import json
+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, "../"))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Required to instantiate the topology builder class.
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ verify_rib,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+
+# TODO: select markers based on daemons used during test
+# pytest module level markers
+"""
+pytestmark = pytest.mark.bfdd # single marker
+pytestmark = [
+ pytest.mark.bgpd,
+ pytest.mark.ospfd,
+ pytest.mark.ospf6d
+] # multiple markers
+"""
+
+
+# Reading the data from JSON File for topology and configuration creation
+jsonFile = "{}/example_topojson.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
+bgp_convergence = False
+input_dict = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # This function only purpose is to create topology
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating 2 routers having single links in between,
+ # which is used to establised BGP neighborship
+
+ # Building topology from json file
+ build_topo_from_json(tgen, topo)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen)
+
+ # This function only purpose is to create configuration
+ # as defined in input json file.
+ #
+ # Example
+ #
+ # Creating configuration defined in input JSON
+ # file, example, BGP config, interface config, static routes
+ # config, prefix list config
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+
+def test_bgp_convergence(request):
+ "Test BGP daemon convergence"
+
+ tgen = get_topogen()
+ global bgp_convergence
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert (
+ bgp_convergence is True
+ ), "test_bgp_convergence failed.. \n" " Error: {}".format(bgp_convergence)
+
+ logger.info("BGP is converged successfully \n")
+ write_test_footer(tc_name)
+
+
+def test_static_routes(request):
+ "Test to create and verify static routes."
+
+ tgen = get_topogen()
+ if bgp_convergence is not True:
+ pytest.skip("skipped because of BGP Convergence failure")
+
+ # test case name
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Static routes are created as part of initial configuration,
+ # verifying RIB
+ dut = "r3"
+ next_hop = ["10.0.0.1", "10.0.0.5"]
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "100.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.1",
+ }
+ ]
+ }
+ }
+ # Uncomment below to debug
+ # tgen.mininet_cli()
+ result = verify_rib(tgen, "ipv4", dut, input_dict, next_hop=next_hop)
+ 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/grpc_basic/lib b/tests/topotests/grpc_basic/lib
new file mode 120000
index 0000000..dc598c5
--- /dev/null
+++ b/tests/topotests/grpc_basic/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/tests/topotests/grpc_basic/r1/zebra.conf b/tests/topotests/grpc_basic/r1/zebra.conf
new file mode 100644
index 0000000..49a0911
--- /dev/null
+++ b/tests/topotests/grpc_basic/r1/zebra.conf
@@ -0,0 +1,8 @@
+log record-priority
+log timestamp precision 6
+log extended extlog
+ destination file ext-log.txt create
+ timestamp precision 6
+ structured-data code-location
+interface r1-eth0
+ ip address 192.168.1.1/24 \ No newline at end of file
diff --git a/tests/topotests/grpc_basic/r2/zebra.conf b/tests/topotests/grpc_basic/r2/zebra.conf
new file mode 100644
index 0000000..20da188
--- /dev/null
+++ b/tests/topotests/grpc_basic/r2/zebra.conf
@@ -0,0 +1,8 @@
+log record-priority
+log timestamp precision 6
+log extended extlog
+ destination file ext-log.txt create
+ timestamp precision 6
+ structured-data code-location
+interface r2-eth0
+ ip address 192.168.1.2/24
diff --git a/tests/topotests/grpc_basic/test_basic_grpc.py b/tests/topotests/grpc_basic/test_basic_grpc.py
new file mode 100644
index 0000000..6bd0be9
--- /dev/null
+++ b/tests/topotests/grpc_basic/test_basic_grpc.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# February 21 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+"""
+test_basic_grpc.py: Test Basic gRPC.
+"""
+
+import logging
+import os
+import sys
+
+import pytest
+
+from lib.common_config import step
+from lib.micronet import commander
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+GRPCP_ZEBRA = 50051
+GRPCP_STATICD = 50052
+GRPCP_BFDD = 50053
+GRPCP_ISISD = 50054
+GRPCP_OSPFD = 50055
+GRPCP_PIMD = 50056
+
+pytestmark = [
+ # pytest.mark.mgmtd -- Need a new non-protocol marker
+ # pytest.mark.bfdd,
+ # pytest.mark.isisd,
+ # pytest.mark.ospfd,
+ # pytest.mark.pimd,
+ pytest.mark.staticd,
+]
+
+script_path = os.path.realpath(os.path.join(CWD, "../lib/grpc-query.py"))
+
+try:
+ commander.cmd_raises([script_path, "--check"])
+except Exception:
+ pytest.skip(
+ "skipping; cannot create or import gRPC proto modules", allow_module_level=True
+ )
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+ topodef = {"s1": ("r1", "r2")}
+ tgen = Topogen(topodef, request.module.__name__)
+
+ tgen.start_topology()
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf", f"-M grpc:{GRPCP_ZEBRA}")
+ router.load_config(TopoRouter.RD_STATIC, None, f"-M grpc:{GRPCP_STATICD}")
+ # router.load_config(TopoRouter.RD_BFD, None, f"-M grpc:{GRPCP_BFDD}")
+ # router.load_config(TopoRouter.RD_ISIS, None, f"-M grpc:{GRPCP_ISISD}")
+ # router.load_config(TopoRouter.RD_OSPF, None, f"-M grpc:{GRPCP_OSPFD}")
+ # router.load_config(TopoRouter.RD_PIM, None, f"-M grpc:{GRPCP_PIMD}")
+
+ tgen.start_router()
+ yield tgen
+
+ logging.info("Stopping all routers (no assert on error)")
+ tgen.stop_topology()
+
+
+# Let's not do this so we catch errors
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ===================
+# The tests functions
+# ===================
+
+
+def run_grpc_client(r, port, commands):
+ if not isinstance(commands, str):
+ commands = "\n".join(commands) + "\n"
+ if not commands.endswith("\n"):
+ commands += "\n"
+ return r.cmd_raises([script_path, f"--port={port}"], stdin=commands)
+
+
+def test_connectivity(tgen):
+ r1 = tgen.gears["r1"]
+ output = r1.cmd_raises("ping -c1 192.168.1.2")
+ logging.info("ping output: %s", output)
+
+
+def test_capabilities(tgen):
+ r1 = tgen.gears["r1"]
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GETCAP")
+ logging.info("grpc output: %s", output)
+
+
+def test_get_config(tgen):
+ nrepeat = 5
+ r1 = tgen.gears["r1"]
+
+ step("'GET' interface config 10 times, once per invocation")
+
+ for i in range(0, nrepeat):
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-interface:lib")
+ logging.info("[iteration %s]: grpc GET output: %s", i, output)
+
+ step(f"'GET' YANG {nrepeat} times in one invocation")
+ commands = ["GET,/frr-interface:lib" for _ in range(0, 10)]
+ output = run_grpc_client(r1, GRPCP_ZEBRA, commands)
+ logging.info("grpc GET*{%d} output: %s", nrepeat, output)
+
+
+def test_get_vrf_config(tgen):
+ r1 = tgen.gears["r1"]
+
+ step("'GET' get VRF config")
+
+ output = run_grpc_client(r1, GRPCP_ZEBRA, "GET,/frr-vrf:lib")
+ logging.info("grpc GET /frr-vrf:lib output: %s", output)
+
+
+def test_shutdown_checks(tgen):
+ # Start a process rnuning that will fetch bunches of data then shut the routers down
+ # and check for cores.
+ nrepeat = 100
+ r1 = tgen.gears["r1"]
+ commands = ["GET,/frr-interface:lib" for _ in range(0, nrepeat)]
+ p = r1.popen([script_path, f"--port={GRPCP_ZEBRA}"] + commands)
+ import time
+
+ time.sleep(1)
+ try:
+ for r in tgen.routers().values():
+ r.net.stopRouter()
+ r.net.checkRouterCores()
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+# Memory leak test template
+# Not compatible with the shutdown check above
+def _test_memory_leak(tgen):
+ "Run the memory leak test and report results."
+
+ 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/isis_lfa_topo1/__init__.py b/tests/topotests/isis_lfa_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/__init__.py
diff --git a/tests/topotests/isis_lfa_topo1/rt1/isisd.conf b/tests/topotests/isis_lfa_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..833cd66
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/isisd.conf
@@ -0,0 +1,52 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis metric 20
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ !fast-reroute lfa tiebreaker node-protecting index 10
+ !fast-reroute lfa tiebreaker downstream index 20
+ !fast-reroute lfa tiebreaker lowest-backup-metric index 30
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..10c61d5
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,236 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d8a7c5a
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,101 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..d626cdc
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step10/show_ipv6_route.ref.diff
@@ -0,0 +1,46 @@
+--- a/rt1/step9/show_ipv6_route.ref
++++ b/rt1/step10/show_ipv6_route.ref
+@@ -16,7 +16,8 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1
++ 1,
++ 2
+ ]
+ }
+ ],
+@@ -30,6 +31,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -198,7 +204,8 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -217,6 +224,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step11/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step11/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..f7f99c2
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step11/show_ipv6_route.ref.diff
@@ -0,0 +1,23 @@
+--- a/rt1/step10/show_ipv6_route.ref
++++ b/rt1/step11/show_ipv6_route.ref
+@@ -204,19 +204,13 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2,
+- 3
++ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step12/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step12/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..3b767f1
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step12/show_ipv6_route.ref.diff
@@ -0,0 +1,107 @@
+--- a/rt1/step11/show_ipv6_route.ref
++++ b/rt1/step12/show_ipv6_route.ref
+@@ -15,9 +15,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2
++ 0
+ ]
+ }
+ ],
+@@ -26,16 +24,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -56,8 +44,7 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -66,11 +53,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -120,10 +102,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -132,21 +111,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -203,19 +167,13 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step13/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step13/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..504af5a
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step13/show_ipv6_route.ref.diff
@@ -0,0 +1,45 @@
+--- a/rt1/step12/show_ipv6_route.ref
++++ b/rt1/step13/show_ipv6_route.ref
+@@ -131,8 +131,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -141,11 +140,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -166,19 +160,13 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..efc56d9
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step2/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step1/show_ipv6_route.ref
++++ b/rt1/step2/show_ipv6_route.ref
+@@ -13,28 +13,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -54,22 +32,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -89,16 +51,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -118,34 +70,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -165,22 +89,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -200,34 +108,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..cafbe49
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step3/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step2/show_ipv6_route.ref
++++ b/rt1/step3/show_ipv6_route.ref
+@@ -13,6 +13,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -32,6 +54,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -51,6 +89,16 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -70,6 +118,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -89,6 +165,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -108,6 +200,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..47d8334
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,142 @@
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -15,9 +15,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2
++ 0
+ ]
+ }
+ ],
+@@ -26,16 +24,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -56,8 +44,7 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -66,11 +53,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -120,10 +102,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -132,21 +111,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -167,8 +131,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1
++ 0
+ ]
+ }
+ ],
+@@ -177,11 +140,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+ }
+ ]
+ }
+@@ -202,10 +160,7 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
++ 0
+ ]
+ }
+ ],
+@@ -214,21 +169,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..b6a342d
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,142 @@
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -15,7 +15,9 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2
+ ]
+ }
+ ],
+@@ -24,6 +26,16 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -44,7 +56,8 @@
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1
+ ]
+ }
+ ],
+@@ -53,6 +66,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
+ }
+ ]
+ }
+@@ -102,7 +120,10 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -111,6 +132,21 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -131,7 +167,8 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1
+ ]
+ }
+ ],
+@@ -140,6 +177,11 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
+ }
+ ]
+ }
+@@ -160,7 +202,10 @@
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+- 0
++ 0,
++ 1,
++ 2,
++ 3
+ ]
+ }
+ ],
+@@ -169,6 +214,21 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..fafa299
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,164 @@
+--- a/rt1/step5/show_ipv6_route.ref
++++ b/rt1/step6/show_ipv6_route.ref
+@@ -13,28 +13,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -54,22 +32,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -89,16 +51,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -118,34 +70,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -165,22 +89,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -200,34 +108,6 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2,
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..1803e2c
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,37 @@
+--- a/rt1/step6/show_ipv6_route.ref
++++ b/rt1/step7/show_ipv6_route.ref
+@@ -108,6 +108,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..306f725
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,129 @@
+--- a/rt1/step7/show_ipv6_route.ref
++++ b/rt1/step8/show_ipv6_route.ref
+@@ -13,6 +13,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -32,6 +54,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -51,6 +89,16 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -70,6 +118,34 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2,
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+@@ -89,6 +165,22 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_lfa_topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_lfa_topo1/rt1/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..3ffab46
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,46 @@
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -16,8 +16,7 @@
+ "active":true,
+ "backupIndex":[
+ 0,
+- 1,
+- 2
++ 1
+ ]
+ }
+ ],
+@@ -31,11 +30,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
+@@ -204,8 +198,7 @@
+ "backupIndex":[
+ 0,
+ 1,
+- 2,
+- 3
++ 2
+ ]
+ }
+ ],
+@@ -224,11 +217,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_lfa_topo1/rt1/zebra.conf b/tests/topotests/isis_lfa_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..4ce1444
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt1/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt2/isisd.conf b/tests/topotests/isis_lfa_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..42dee00
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/isisd.conf
@@ -0,0 +1,39 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..036bfe1
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,162 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..681c522
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt2/zebra.conf b/tests/topotests/isis_lfa_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..3372ec5
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt2/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt3/isisd.conf b/tests/topotests/isis_lfa_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..9dd813c
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt3/isisd.conf
@@ -0,0 +1,38 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..a1aab40
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,188 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..1495e32
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt3/zebra.conf b/tests/topotests/isis_lfa_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..231b02b
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt3/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt4/isisd.conf b/tests/topotests/isis_lfa_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..7500ff8
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt4/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..6878e2f
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,172 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":35,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d8cd565
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt4/zebra.conf b/tests/topotests/isis_lfa_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..2d62924
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt4/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt5/isisd.conf b/tests/topotests/isis_lfa_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..5e022e9
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt5/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis metric 20
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..f8181c7
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,176 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":35,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d8cd565
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt5/zebra.conf b/tests/topotests/isis_lfa_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..efb7bf6
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt5/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt6/isisd.conf b/tests/topotests/isis_lfa_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..d262e8a
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt6/isisd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt7
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..e5f3c77
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,172 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d8cd565
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt7",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0007",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt6/zebra.conf b/tests/topotests/isis_lfa_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..31650bd
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt6/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt7/isisd.conf b/tests/topotests/isis_lfa_topo1/rt7/isisd.conf
new file mode 100644
index 0000000..c206123
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt7/isisd.conf
@@ -0,0 +1,51 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ipv6 router isis 1
+ isis metric 5
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt3
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt4
+ ipv6 router isis 1
+ isis metric 15
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt5
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+interface eth-rt6
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute lfa
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lfa_topo1/rt7/step1/show_ipv6_route.ref b/tests/topotests/isis_lfa_topo1/rt7/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..0dff15e
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt7/step1/show_ipv6_route.ref
@@ -0,0 +1,186 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2,
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":15,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":25,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lfa_topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d8a7c5a
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt7/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,101 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lfa_topo1/rt7/zebra.conf b/tests/topotests/isis_lfa_topo1/rt7/zebra.conf
new file mode 100644
index 0000000..4271cce
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/rt7/zebra.conf
@@ -0,0 +1,16 @@
+log file zebra.log
+!
+hostname rt7
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 7.7.7.7/32
+ ipv6 address 2001:db8:1000::7/128
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py b/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
new file mode 100755
index 0000000..7e90221
--- /dev/null
+++ b/tests/topotests/isis_lfa_topo1/test_isis_lfa_topo1.py
@@ -0,0 +1,631 @@
+#!/usr/bin/env python
+
+#
+# test_isis_tilfa_topo1.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_isis_lfa_topo1.py:
+
+ +---------+
+ | |
+ +--------------------------------+ RT1 +-------------------------------+
+ | +-------------+ +-------------+ |
+ | | | | | |
+ | | +----+----+ | |
+ | | | |20 |
+ | | | | |
+ | | | | |
+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
+ | | | | | | | | | |
+ | RT2 | 5 | RT3 | | RT4 | | RT5 | | RT6 |
+ | +--------+ | | | | | | |
+ | | | | | | | | | |
+ +----+----+ +----+----+ +----+----+ +----+----+ +----+----+
+ | | | | |
+ | | |15 | |
+ |5 | | | |
+ | | +----+----+ | |
+ | | | | | |
+ | +-------------+ RT7 +-------------+ |
+ +--------------------------------+ +-------------------------------+
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import tempfile
+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.
+
+pytestmark = [pytest.mark.isisd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt2")
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt3")
+ switch = tgen.add_switch("s9")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt4")
+ switch = tgen.add_switch("s10")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
+ switch = tgen.add_switch("s11")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = ["show_ipv6_route.ref", "show_yang_interface_isis_adjacencies.ref"]
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ outputs[rname] = {}
+ for step in range(1, 13 + 1):
+ outputs[rname][step] = {}
+ for file in files:
+ if step == 1:
+ # Get snapshots relative to the expected initial network convergence
+ filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
+ outputs[rname][step][file] = open(filename).read()
+ else:
+ if rname != "rt1":
+ continue
+ if file == "show_yang_interface_isis_adjacencies.ref":
+ continue
+
+ # Get diff relative to the previous step
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
+
+ # Create temporary files in order to apply the diff
+ f_in = tempfile.NamedTemporaryFile(mode="w")
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile(mode="r")
+ os.system(
+ "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
+ )
+
+ # Store the updated snapshot and remove the temporary files
+ outputs[rname][step][file] = open(f_out.name).read()
+ f_in.close()
+ f_out.close()
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ expected = json.loads(reference)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable LFA protection on all interfaces
+#
+# Expected changes:
+# -rt1 should uninstall all backup nexthops from all routes
+#
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling LFA protection on all rt1 interfaces")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt4" -c "no isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "no isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt6" -c "no isis fast-reroute lfa"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Re-enable LFA protection on all interfaces
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-enabling LFA protection on all rt1 interfaces")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt4" -c "isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "isis fast-reroute lfa"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt6" -c "isis fast-reroute lfa"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable LFA load-sharing
+#
+# Expected changes:
+# -rt1 should use at most one backup nexthop for each route
+#
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling LFA load-sharing on rt1")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute load-sharing disable"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Re-enable LFA load-sharing
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-enabling LFA load-sharing on rt1")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute load-sharing disable"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Limit backup computation to critical priority prefixes only
+#
+# Expected changes:
+# -rt1 should uninstall all backup nexthops from all routes
+#
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Limiting backup computation to critical priority prefixes only")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute priority-limit critical"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Configure a prefix priority list to classify rt7's loopback as a
+# critical-priority prefix
+#
+# Expected changes:
+# -rt1 should install backup nexthops for rt7's loopback route.
+#
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring a prefix priority list")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "spf prefix-priority critical CRITICAL_DESTINATIONS"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Revert previous changes related to prefix priorities
+#
+# Expected changes:
+# -Revert changes from the previous two steps
+#
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Reverting previous changes related to prefix priorities")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "no ipv6 access-list CRITICAL_DESTINATIONS seq 5 permit 2001:db8:1000::7/128"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no fast-reroute priority-limit critical"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no spf prefix-priority critical CRITICAL_DESTINATIONS"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Exclude eth-rt6 from LFA computation for eth-rt2's failure
+#
+# Expected changes:
+# -Uninstall the eth-rt2 protecting backup nexthops that go through eth-rt6
+#
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Excluding eth-rt6 from LFA computation for eth-rt2's failure")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute lfa exclude interface eth-rt6"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -Remove exclusion of eth-rt6 from LFA computation for eth-rt2's failure
+#
+# Expected changes:
+# -Revert changes from the previous step
+#
+def test_rib_ipv6_step10():
+ logger.info("Test (step 10): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Removing exclusion of eth-rt6 from LFA computation for eth-rt2's failure"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute lfa exclude interface eth-rt6"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 11
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer node protecting backup path
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that provide node protection
+#
+def test_rib_ipv6_step11():
+ logger.info("Test (step 11): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Adding LFA tiebreaker: prefer node protecting backup path")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker node-protecting index 10"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][11]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 12
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer backup path via downstream node
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that satisfy the downstream condition
+#
+def test_rib_ipv6_step12():
+ logger.info("Test (step 12): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Adding LFA tiebreaker: prefer backup path via downstream node")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker downstream index 20"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][12]["show_ipv6_route.ref"],
+ )
+
+
+#
+# Step 13
+#
+# Action(s):
+# -Add LFA tiebreaker: prefer backup path with lowest total metric
+#
+# Expected changes:
+# -rt1 should prefer backup nexthops that have the best metric
+#
+def test_rib_ipv6_step13():
+ logger.info("Test (step 13): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Adding LFA tiebreaker: prefer backup path with lowest total metric")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute lfa tiebreaker lowest-backup-metric index 30"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][13]["show_ipv6_route.ref"],
+ )
+
+
+# Memory leak test template
+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/isis_lsp_bits_topo1/__init__.py b/tests/topotests/isis_lsp_bits_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/__init__.py
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..fc004e4
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/isisd.conf
@@ -0,0 +1,27 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis priority 100
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..8557f4b
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,89 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..fa76533
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,65 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..26f0dff
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,32 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ip_route.ref
new file mode 100644
index 0000000..c826efd
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ip_route.ref
@@ -0,0 +1,82 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..a386b45
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step2/show_ipv6_route.ref
@@ -0,0 +1,59 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ip_route.ref
new file mode 100644
index 0000000..2b281b7
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ip_route.ref
@@ -0,0 +1,62 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..4b920ed
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step3/show_ipv6_route.ref
@@ -0,0 +1,40 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ip_route.ref
new file mode 100644
index 0000000..8557f4b
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ip_route.ref
@@ -0,0 +1,89 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..fa76533
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/step4/show_ipv6_route.ref
@@ -0,0 +1,65 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..37b3f27
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt1/zebra.conf
@@ -0,0 +1,19 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..d01720f
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/isisd.conf
@@ -0,0 +1,35 @@
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 2
+ net 49.0002.0000.0000.0002.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000..d7e886c
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ip_route.ref
@@ -0,0 +1,77 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..a92272f
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,40 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..c70b44e
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,58 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..03acb6f
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4
+ ip address 10.0.2.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..a8d5896
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/isisd.conf
@@ -0,0 +1,35 @@
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 2
+ net 49.0002.0000.0000.0003.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000..55f0aed
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ip_route.ref
@@ -0,0 +1,97 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..5d6dfca
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,59 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..6950086
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,51 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..5f59be1
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+!
+interface eth-rt5
+ ip address 10.0.4.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..2d30790
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 4
+ ipv6 router isis 4
+ isis passive
+!
+interface eth-rt2
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 2
+ net 49.0002.0000.0000.0004.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 4
+ net 49.0004.0000.0000.0004.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000..2cf5c40
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ip_route.ref
@@ -0,0 +1,94 @@
+{
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..cde7287
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,21 @@
+{
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..233180c
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..a567f3b
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt4/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt2
+ ip address 10.0.2.4/24
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..263c3f9
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 2
+ ipv6 router isis 2
+ isis passive
+!
+interface eth-rt3
+ ip router isis 2
+ ipv6 router isis 2
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 2
+ net 49.0002.0000.0000.0005.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
+router isis 4
+ net 49.0004.0000.0000.0005.00
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000..358d0a2
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,116 @@
+{
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "weight":1
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..7586c73
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,40 @@
+{
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..f939a6a
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..50aedb3
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt5/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt3
+ ip address 10.0.4.5/24
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf b/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..505604e
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/isisd.conf
@@ -0,0 +1,32 @@
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 4
+ ipv6 router isis 4
+ isis passive
+!
+interface eth-rt4
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 4
+ ipv6 router isis 4
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 4
+ net 49.0004.0000.0000.0006.00
+ is-type level-1
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000..4037579
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,106 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..278129f
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,46 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..b4e8c23
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ip_route.ref
new file mode 100644
index 0000000..8083be4
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ip_route.ref
@@ -0,0 +1,99 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..2e982e0
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step2/show_ipv6_route.ref
@@ -0,0 +1,40 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ip_route.ref
new file mode 100644
index 0000000..1ba8c8c
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ip_route.ref
@@ -0,0 +1,79 @@
+{
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..9b53a1d
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step3/show_ipv6_route.ref
@@ -0,0 +1,21 @@
+{
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ip_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ip_route.ref
new file mode 100644
index 0000000..4037579
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ip_route.ref
@@ -0,0 +1,106 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ipv6_route.ref b/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..278129f
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/step4/show_ipv6_route.ref
@@ -0,0 +1,46 @@
+{
+ "::\/0":[
+ {
+ "prefix":"::\/0",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf b/tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..4d51d3d
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py b/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
new file mode 100755
index 0000000..9b4cd95
--- /dev/null
+++ b/tests/topotests/isis_lsp_bits_topo1/test_isis_lsp_bits_topo1.py
@@ -0,0 +1,377 @@
+#!/usr/bin/env python
+
+#
+# test_isis_lsp_bits_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Volta Networks
+#
+# 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_isis_lsp_bits_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | L1 |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | L1|L2 | 10.0.1.0/24 | L1|L2 |
+ +---------+ +---------+
+ eth-rt4| eth-rt5|
+ | |
+ 10.0.2.0/24| |10.0.4.0/24
+ | |
+ eth-rt2| eth-rt3|
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | L1|L2 |eth-rt5 eth-rt4| L1|L2 |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| L1 |eth-rt5
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.isisd]
+
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+# Attach-bit defaults to on, so expect default route pointing to L1|L2 router
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step1/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step1/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step1/show_ipv6_route.ref"
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable sending Attach bit on RT2 and RT4
+#
+# Expected changes:
+# -RT1 should remove the default route pointing to RT2
+# -RT6 should remove the default route pointing to RT4
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling setting the attached-bit on RT2 and RT4")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no attached-bit send"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no attached-bit send"'
+ )
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step2/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step2/show_ipv6_route.ref"
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -restore attach-bit, enable sending attach-bit
+# -disble processing a LSP with attach bit set
+#
+# Expected changes:
+# -RT1 and RT6 should not install a default route
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enable setting the attached-bit on RT2 and RT4")
+ tgen.net["rt2"].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit send"')
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "router isis 1" -c "attached-bit send"')
+
+ logger.info("Disable processing received attached-bit in LSP on RT1 and RT6")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "attached-bit receive ignore"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "attached-bit receive ignore"'
+ )
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step3/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step3/show_ipv6_route.ref"
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -restore back to default attach-bit config
+#
+# Expected changes:
+# -RT1 and RT6 should add default route
+# -no changes on other routers
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "restore default processing on received attached-bit in LSP on RT1 and RT6"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no attached-bit receive ignore"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no attached-bit receive ignore"'
+ )
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step4/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step4/show_ipv6_route.ref"
+ )
+
+
+# Memory leak test template
+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/isis_rlfa_topo1/__init__.py b/tests/topotests/isis_rlfa_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/__init__.py
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..f7f0a95
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/isisd.conf
@@ -0,0 +1,39 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+ isis fast-reroute lfa
+ isis fast-reroute remote-lfa tunnel mpls-ldp
+!
+ip prefix-list PLIST seq 5 permit 10.0.255.8/32
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+ fast-reroute remote-lfa prefix-list PLIST
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf
new file mode 100644
index 0000000..eb85892
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt1
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.1
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::1
+ !
+ interface eth-rt2
+ interface eth-rt3
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..680b31e
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,235 @@
+{
+ "10.0.255.2\/32":[
+ {
+ "prefix":"10.0.255.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.3\/32":[
+ {
+ "prefix":"10.0.255.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.4\/32":[
+ {
+ "prefix":"10.0.255.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.5\/32":[
+ {
+ "prefix":"10.0.255.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.6\/32":[
+ {
+ "prefix":"10.0.255.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.7\/32":[
+ {
+ "prefix":"10.0.255.7\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "10.0.255.8\/32":[
+ {
+ "prefix":"10.0.255.8\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.255.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "onLink":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.255.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..c487d27
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,207 @@
+{
+ "2001:db8::2\/128":[
+ {
+ "prefix":"2001:db8::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::3\/128":[
+ {
+ "prefix":"2001:db8::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::4\/128":[
+ {
+ "prefix":"2001:db8::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::5\/128":[
+ {
+ "prefix":"2001:db8::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::6\/128":[
+ {
+ "prefix":"2001:db8::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::7\/128":[
+ {
+ "prefix":"2001:db8::7\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true,
+ "labels":"*"
+ }
+ ]
+ }
+ ],
+ "2001:db8::8\/128":[
+ {
+ "prefix":"2001:db8::8\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..3fe2b79
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1-2",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ip_route.ref.diff
new file mode 100644
index 0000000..ef5707f
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step9/show_ip_route.ref
++++ b/rt1/step10/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -125,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..acd2ce0
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step10/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step9/show_ipv6_route.ref
++++ b/rt1/step10/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -62,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -111,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..f7f31ac
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step1/show_ip_route.ref
++++ b/rt1/step2/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -49,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +57,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -117,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +99,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -185,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e980031
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step2/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step1/show_ipv6_route.ref
++++ b/rt1/step2/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -43,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +51,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -103,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +89,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -163,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..f3ed764
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step2/show_ip_route.ref
++++ b/rt1/step3/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..57b0b1d
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step3/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step2/show_ipv6_route.ref
++++ b/rt1/step3/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..107a0ba
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step3/show_ip_route.ref
++++ b/rt1/step4/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..9cf2408
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..0946950
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step4/show_ip_route.ref
++++ b/rt1/step5/show_ip_route.ref
+@@ -36,20 +36,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -91,20 +78,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -146,20 +120,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..70fb1a6
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -32,18 +32,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -81,18 +70,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -130,18 +108,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt2",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..4e4a569
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ip_route.ref.diff
@@ -0,0 +1,134 @@
+--- a/rt1/step5/show_ip_route.ref
++++ b/rt1/step6/show_ip_route.ref
+@@ -15,7 +15,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -36,7 +49,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -57,7 +83,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -78,7 +117,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -99,7 +151,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -120,7 +185,20 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3",
+ "active":true,
+- "onLink":true
++ "onLink":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.255.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "onLink":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..c9ebb1e
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,122 @@
+--- a/rt1/step5/show_ipv6_route.ref
++++ b/rt1/step6/show_ipv6_route.ref
+@@ -13,7 +13,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -32,7 +43,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -51,7 +73,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -70,7 +103,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -89,7 +133,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt3",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
+@@ -108,7 +163,18 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt2",
++ "active":true,
++ "labels":"*"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step7/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step8/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ip_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..33eb657
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ip_route.ref.diff
@@ -0,0 +1,68 @@
+--- a/rt1/step8/show_ip_route.ref
++++ b/rt1/step9/show_ip_route.ref
+@@ -15,20 +15,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -83,20 +70,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
+@@ -151,20 +125,7 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2",
+ "active":true,
+- "onLink":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.255.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "onLink":true,
+- "labels":"*"
++ "onLink":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..7aaca33
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,62 @@
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -13,18 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -73,18 +62,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
+@@ -133,18 +111,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt3",
+- "active":true,
+- "labels":"*"
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_rlfa_topo1/rt1/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..6210b29
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt1/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.1/32
+ ipv6 address 2001:db8::1/128
+!
+interface eth-rt2
+ ip address 10.0.255.1/32
+!
+interface eth-rt3
+ ip address 10.0.255.1/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..6595052
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt2/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf
new file mode 100644
index 0000000..25882be
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt2/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt2
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.2
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::2
+ !
+ interface eth-rt1
+ interface eth-rt4
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt2/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..38c561d
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.2/32
+ ipv6 address 2001:db8::2/128
+!
+interface eth-rt1
+ ip address 10.0.255.2/32
+!
+interface eth-rt4
+ ip address 10.0.255.2/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..a029b71
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt3/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf
new file mode 100644
index 0000000..8f2234a
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt3/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt3
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.3
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::3
+ !
+ interface eth-rt1
+ interface eth-rt5
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt3/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..2155764
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.3/32
+ ipv6 address 2001:db8::3/128
+!
+interface eth-rt1
+ ip address 10.0.255.3/32
+!
+interface eth-rt5
+ ip address 10.0.255.3/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..86aa6b1
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt4/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf
new file mode 100644
index 0000000..c8d467c
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt4/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt4
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.4
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::4
+ !
+ interface eth-rt2
+ interface eth-rt6
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt4/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..4e22813
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.4/32
+ ipv6 address 2001:db8::4/128
+!
+interface eth-rt2
+ ip address 10.0.255.4/32
+!
+interface eth-rt6
+ ip address 10.0.255.4/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..e4fc9cd
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt5/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf
new file mode 100644
index 0000000..c5fc36a
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt5/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt5
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.5
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::5
+ !
+ interface eth-rt3
+ interface eth-rt7
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt5/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..83bbb1b
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt5/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.5/32
+ ipv6 address 2001:db8::5/128
+!
+interface eth-rt3
+ ip address 10.0.255.5/32
+!
+interface eth-rt7
+ ip address 10.0.255.5/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..81319e4
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt6/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf
new file mode 100644
index 0000000..11bed3c
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt6/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt6
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.6
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::6
+ !
+ interface eth-rt4
+ interface eth-rt8
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt6/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..1fdd0d4
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.6/32
+ ipv6 address 2001:db8::6/128
+!
+interface eth-rt4
+ ip address 10.0.255.6/32
+!
+interface eth-rt8
+ ip address 10.0.255.6/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf
new file mode 100644
index 0000000..1f48671
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt7/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt7
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt8
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0007.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf
new file mode 100644
index 0000000..6c40ccb
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt7/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt7
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.7
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::7
+ !
+ interface eth-rt5
+ interface eth-rt8
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt7/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt7/zebra.conf
new file mode 100644
index 0000000..114ebd9
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt7/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt7
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.7/32
+ ipv6 address 2001:db8::7/128
+!
+interface eth-rt5
+ ip address 10.0.255.7/32
+!
+interface eth-rt8
+ ip address 10.0.255.7/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf b/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf
new file mode 100644
index 0000000..7675f77
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt8/isisd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt8
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface eth-rt7
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+router isis 1
+ net 49.0000.0000.0000.0008.00
+ is-type level-1-2
+ lsp-gen-interval 2
+ topology ipv6-unicast
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf b/tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf
new file mode 100644
index 0000000..36e7ce1
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt8/ldpd.conf
@@ -0,0 +1,30 @@
+log file ldpd.log
+!
+hostname rt8
+!
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp zebra
+!
+mpls ldp
+ router-id 10.0.255.8
+ dual-stack transport-connection prefer ipv4
+ !
+ address-family ipv4
+ label local allocate host-routes
+ discovery targeted-hello accept
+ discovery transport-address 10.0.255.8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+ address-family ipv6
+ label local allocate host-routes
+ discovery transport-address 2001:db8::8
+ !
+ interface eth-rt6
+ interface eth-rt7
+ !
+ !
+!
diff --git a/tests/topotests/isis_rlfa_topo1/rt8/zebra.conf b/tests/topotests/isis_rlfa_topo1/rt8/zebra.conf
new file mode 100644
index 0000000..001e62e
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/rt8/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt8
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 10.0.255.8/32
+ ipv6 address 2001:db8::8/128
+!
+interface eth-rt6
+ ip address 10.0.255.8/32
+!
+interface eth-rt7
+ ip address 10.0.255.8/32
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py b/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
new file mode 100755
index 0000000..ba0543a
--- /dev/null
+++ b/tests/topotests/isis_rlfa_topo1/test_isis_rlfa_topo1.py
@@ -0,0 +1,657 @@
+#!/usr/bin/env python
+
+#
+# test_isis_rlfa_topo1.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_isis_rlfa_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | +---------------------+ |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT3 | | RT4 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT5 | | RT6 |
+ | | | |
+ | | | |
+ +---+-----+ +------+--+
+ | |
+ | |
+ | |
+ +---+-----+ +------+--+
+ | | | |
+ | RT7 | | RT8 |
+ | +---------------------+ |
+ | | | |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import tempfile
+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.
+
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7", "rt8"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt5")
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt6")
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt8")
+ switch.add_link(tgen.gears["rt8"], nodeif="eth-rt7")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = [
+ "show_ip_route.ref",
+ "show_ipv6_route.ref",
+ "show_yang_interface_isis_adjacencies.ref",
+ ]
+ for rname in ["rt1"]:
+ outputs[rname] = {}
+ for step in range(1, 10 + 1):
+ outputs[rname][step] = {}
+ for file in files:
+ if step == 1:
+ # Get snapshots relative to the expected initial network convergence
+ filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
+ outputs[rname][step][file] = open(filename).read()
+ else:
+ if file == "show_yang_interface_isis_adjacencies.ref":
+ continue
+
+ # Get diff relative to the previous step
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
+
+ # Create temporary files in order to apply the diff
+ f_in = tempfile.NamedTemporaryFile(mode="w")
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile(mode="r")
+ os.system(
+ "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
+ )
+
+ # Store the updated snapshot and remove the temporary files
+ outputs[rname][step][file] = open(f_out.name).read()
+ f_in.close()
+ f_out.close()
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ expected = json.loads(reference)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
+ )
+
+
+def test_rib_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Configure rt8 (rt1's PQ router) to not accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 backup routes should be uninstalled
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to not accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "no discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Configure rt8 (rt1's PQ router) to accept targeted hello messages
+#
+# Expected changes:
+# -All rt1 previously uninstalled backup routes should be reinstalled
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring rt8 to accept targeted hello messages")
+ tgen.net["rt8"].cmd(
+ 'vtysh -c "conf t" -c "mpls ldp" -c "address-family ipv4" -c "discovery targeted-hello accept"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable RLFA on rt1's eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt2 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Disable RLFA on rt1's eth-rt3 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt3 should lose their backup nexthops
+#
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling RLFA on rt1's eth-rt3 interface")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "no isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Re-enable RLFA on rt1's eth-rt2 and eth-rt3 interfaces
+#
+# Expected changes:
+# -Revert changes from the previous two steps (reinstall all backup routes)
+#
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-enabling RLFA on rt1's eth-rt2 and eth-rt3 interfaces")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt3" -c "isis fast-reroute remote-lfa tunnel mpls-ldp"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Configure a PQ node prefix-list filter
+#
+# Expected changes:
+# -All backup routes should be uninstalled
+#
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring a PQ node prefix-list filter")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "fast-reroute remote-lfa prefix-list PLIST"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Configure a prefix-list allowing rt8 as a PQ node
+#
+# Expected changes:
+# -All backup routes should be installed again
+#
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Configuring a prefix-list allowing rt8 as a PQ node")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "ip prefix-list PLIST seq 5 permit 10.0.255.8/32"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Change the maximum metric up to the PQ node to 30 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should lose their backup nexthops
+#
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 30 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 30"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -Change the maximum metric up to the PQ node to 40 on the eth-rt2 interface
+#
+# Expected changes:
+# -All non-ECMP routes whose primary nexthop is eth-rt2 should recover their backup nexthops
+#
+def test_rib_ipv4_step10():
+ logger.info("Test (step 10): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info(
+ "Changing the maximum metric up to the PQ node to 40 on the eth-rt2 interface"
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2" -c "isis fast-reroute remote-lfa maximum-metric 40"'
+ )
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][10]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step10():
+ logger.info("Test (step 10): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1"]:
+ router_compare_json_output(
+ rname,
+ "show ipv6 route isis json",
+ outputs[rname][10]["show_ipv6_route.ref"],
+ )
+
+
+# Memory leak test template
+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/isis_snmp/ce3/zebra.conf b/tests/topotests/isis_snmp/ce3/zebra.conf
new file mode 100644
index 0000000..c6a5824
--- /dev/null
+++ b/tests/topotests/isis_snmp/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r1/isisd.conf b/tests/topotests/isis_snmp/r1/isisd.conf
new file mode 100644
index 0000000..4928341
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/isisd.conf
@@ -0,0 +1,24 @@
+hostname r1
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r1-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r1-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis_snmp/r1/ldpd.conf b/tests/topotests/isis_snmp/r1/ldpd.conf
new file mode 100644
index 0000000..5b1cbfe
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/ldpd.conf
@@ -0,0 +1,26 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+agentx
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth0
+ !
+ interface r1-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r1/show_ip_route.ref b/tests/topotests/isis_snmp/r1/show_ip_route.ref
new file mode 100644
index 0000000..dc8f19d
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_snmp/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_snmp/r1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..390fda7
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r1-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r1-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_snmp/r1/snmpd.conf b/tests/topotests/isis_snmp/r1/snmpd.conf
new file mode 100644
index 0000000..3fd5e98
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/isis_snmp/r1/zebra.conf b/tests/topotests/isis_snmp/r1/zebra.conf
new file mode 100644
index 0000000..4ca46da
--- /dev/null
+++ b/tests/topotests/isis_snmp/r1/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r1
+!
+! debug zebra kernel
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra nht
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to rt4
+ ip address 14.0.0.1/24
+!
+interface r1-eth1
+ description to rt3
+ ip address 13.0.0.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r2/isisd.conf b/tests/topotests/isis_snmp/r2/isisd.conf
new file mode 100644
index 0000000..9bb8a8d
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/isisd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r2-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r2-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis_snmp/r2/ldpd.conf b/tests/topotests/isis_snmp/r2/ldpd.conf
new file mode 100644
index 0000000..533d2d9
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r2/show_ip_route.ref b/tests/topotests/isis_snmp/r2/show_ip_route.ref
new file mode 100644
index 0000000..2bcee96
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_snmp/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_snmp/r2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..52550da
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r2-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r2-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_snmp/r2/snmpd.conf b/tests/topotests/isis_snmp/r2/snmpd.conf
new file mode 100644
index 0000000..fc64805
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:2.2.2.2:161
+
+com2sec public 2.2.2.2 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/isis_snmp/r2/zebra.conf b/tests/topotests/isis_snmp/r2/zebra.conf
new file mode 100644
index 0000000..9f6e3d5
--- /dev/null
+++ b/tests/topotests/isis_snmp/r2/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r2
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to rt5
+ ip address 25.0.0.2/24
+!
+interface r2-eth1
+ description to rt3
+ ip address 23.0.0.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r3/isisd.conf b/tests/topotests/isis_snmp/r3/isisd.conf
new file mode 100644
index 0000000..4daec79
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/isisd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r3-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis_snmp/r3/ldpd.conf b/tests/topotests/isis_snmp/r3/ldpd.conf
new file mode 100644
index 0000000..fae25e0
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r3/show_ip_route.ref b/tests/topotests/isis_snmp/r3/show_ip_route.ref
new file mode 100644
index 0000000..da46f1d
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_snmp/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_snmp/r3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..3aafab4
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r3-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r3-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_snmp/r3/snmpd.conf b/tests/topotests/isis_snmp/r3/snmpd.conf
new file mode 100644
index 0000000..20af65e
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:3.3.3.3:161
+
+com2sec public 3.3.3.3 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/isis_snmp/r3/zebra.conf b/tests/topotests/isis_snmp/r3/zebra.conf
new file mode 100644
index 0000000..4e6bddd
--- /dev/null
+++ b/tests/topotests/isis_snmp/r3/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r3
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to ce3
+ ip address 172.16.1.3/24
+!
+interface r3-eth1
+ description to rt2
+ ip address 13.0.0.3/24
+!
+interface r3-eth2
+ description to rt1
+ ip address 23.0.0.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r4/isisd.conf b/tests/topotests/isis_snmp/r4/isisd.conf
new file mode 100644
index 0000000..32b07b3
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/isisd.conf
@@ -0,0 +1,24 @@
+hostname r4
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r4-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r4-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis_snmp/r4/ldpd.conf b/tests/topotests/isis_snmp/r4/ldpd.conf
new file mode 100644
index 0000000..dbffdff
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r4
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 4.4.4.4
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r4-eth0
+ !
+ interface r4-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r4/show_ip_route.ref b/tests/topotests/isis_snmp/r4/show_ip_route.ref
new file mode 100644
index 0000000..da46f1d
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_snmp/r4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_snmp/r4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..86fcfea
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r4-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r4-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_snmp/r4/snmpd.conf b/tests/topotests/isis_snmp/r4/snmpd.conf
new file mode 100644
index 0000000..76e4b79
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:4.4.4.4:161
+
+com2sec public 4.4.4.4 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/isis_snmp/r4/zebra.conf b/tests/topotests/isis_snmp/r4/zebra.conf
new file mode 100644
index 0000000..66b8ea3
--- /dev/null
+++ b/tests/topotests/isis_snmp/r4/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r4
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to rt1
+ ip address 14.0.0.4/24
+!
+interface r4-eth1
+ description to rt5
+ ip address 45.0.0.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r5/isisd.conf b/tests/topotests/isis_snmp/r5/isisd.conf
new file mode 100644
index 0000000..fe3ca0f
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/isisd.conf
@@ -0,0 +1,25 @@
+hostname r5
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+agentx
+!
+router isis 1
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
+interface r5-eth0
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r5-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/isis_snmp/r5/ldpd.conf b/tests/topotests/isis_snmp/r5/ldpd.conf
new file mode 100644
index 0000000..fd273d5
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r5
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 5.5.5.5
+ !
+ address-family ipv4
+ discovery transport-address 5.5.5.5
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r5-eth0
+ !
+ interface r5-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r5/ldpdconf b/tests/topotests/isis_snmp/r5/ldpdconf
new file mode 100644
index 0000000..fc70060
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/ldpdconf
@@ -0,0 +1,25 @@
+hostname r5
+log file ldpd.log
+!
+debug mpls ldp zebra
+debug mpls ldp event
+debug mpls ldp errors
+debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 5.5.5.5
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r5-eth0
+ !
+ interface r5-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/r5/show_ip_route.ref b/tests/topotests/isis_snmp/r5/show_ip_route.ref
new file mode 100644
index 0000000..da46f1d
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/show_ip_route.ref
@@ -0,0 +1,143 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":1,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":3,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceIndex":4,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_snmp/r5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_snmp/r5/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..994e816
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,40 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r5-eth0",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r5-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_snmp/r5/snmpd.conf b/tests/topotests/isis_snmp/r5/snmpd.conf
new file mode 100644
index 0000000..af59194
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:5.5.5.5:161
+
+com2sec public 5.5.5.5 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/isis_snmp/r5/zebra.conf b/tests/topotests/isis_snmp/r5/zebra.conf
new file mode 100644
index 0000000..5607fab
--- /dev/null
+++ b/tests/topotests/isis_snmp/r5/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname r5
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface r5-eth0
+ description to rt2
+ ip address 25.0.0.5/24
+!
+interface r5-eth1
+ description to rt4
+ ip address 45.0.0.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_snmp/test_isis_snmp.dot b/tests/topotests/isis_snmp/test_isis_snmp.dot
new file mode 100644
index 0000000..6d8c893
--- /dev/null
+++ b/tests/topotests/isis_snmp/test_isis_snmp.dot
@@ -0,0 +1,114 @@
+## 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="Test Topology - LDP-VPLS 1";
+
+ # Routers
+ ce3 [
+ shape=doubleoctagon
+ label="ce3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ 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,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="s1\n14.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="s2\n25.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="s3\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="s4\n45.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ shape=oval,
+ label="s5\n13.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ shape=oval,
+ label="s6\n23.0.0.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ ce3 -- s3 [label="eth0\n.3"];
+
+ r1 -- s1 [label="eth1\n.1"];
+ r1 -- s5 [label="eth2\n.1"];
+
+ r2 -- s2 [label="eth1\n.2"];
+ r2 -- s6 [label="eth2\n.2"];
+
+ r3 -- s5 [label="eth1\n.3"];
+ r3 -- s6 [label="eth2\n.3"];
+
+ r4 -- s1 [label="eth1\n.4"];
+ r4 -- s4 [label="eth2\n.4"];
+
+ r5 -- s2 [label="eth1\n.5"];
+ r5 -- s4 [label="eth2\n.5"];
+}
diff --git a/tests/topotests/isis_snmp/test_isis_snmp.py b/tests/topotests/isis_snmp/test_isis_snmp.py
new file mode 100755
index 0000000..206291a
--- /dev/null
+++ b/tests/topotests/isis_snmp/test_isis_snmp.py
@@ -0,0 +1,374 @@
+#!/usr/bin/env python
+
+#
+# test_isis_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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_isis_snmp.py:
+
+ +---------+ 45.0.0.0/24 +---------+
+ | | rt4-eth1 | |
+ | RT4 +----------------+ RT5 |
+ | | rt5-eth1| |
+ +---------+ +---------+
+ rt4-eth0| |rt5-eth0
+ | |
+ 14.0.0.0/24| |25.0.0.0/24
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ +---------+
+ | | | |
+ | RT1 | | RT2 |
+ | 1.1.1.1 | | 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth1| |rt2-eth1
+ | |
+ | |
+ 13.0.0.0/24| +---------+ |23.0.0.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth1| |rt3-eth2
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.snmptest import SnmpTester
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd, pytest.mark.snmp]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce3", "r1", "r2", "r3", "r4", "r5"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r5"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r5"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # skip tests is SNMP not installed
+ if not os.path.isfile("/usr/sbin/snmpd"):
+ error_msg = "SNMP not installed - skipping"
+ pytest.skip(error_msg)
+
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start the following in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_ISIS,
+ os.path.join(CWD, "{}/isisd.conf".format(rname)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_LDP,
+ os.path.join(CWD, "{}/ldpd.conf".format(rname)),
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP,
+ os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap",
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ 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)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def generate_oid(numoids, index1, index2):
+ if numoids == 1:
+ oid = "{}".format(index1)
+ else:
+ oid = "{}.{}".format(index1, index2)
+ return oid
+
+
+def test_isis_convergence():
+ logger.info("Test: check ISIS adjacencies")
+ tgen = get_topogen()
+
+ for rname in ["r1", "r2", "r3", "r4", "r5"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_r1_scalar_snmp():
+ "Wait for protocol convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid("isisSysVersion", "one(1)")
+ assert r1_snmp.test_oid("isisSysLevelType", "level1and2(3)")
+ assert r1_snmp.test_oid("isisSysID", "00 00 00 00 00 01")
+ assert r1_snmp.test_oid("isisSysMaxPathSplits", "32")
+ assert r1_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds")
+ assert r1_snmp.test_oid("isisSysAdminState", "on(1)")
+ assert r1_snmp.test_oid("isisSysMaxAge", "1200 seconds")
+ assert r1_snmp.test_oid("isisSysProtSupported", "07 5 6 7")
+
+ r2 = tgen.gears["r2"]
+ r2_snmp = SnmpTester(r2, "2.2.2.2", "public", "2c")
+
+ assert r2_snmp.test_oid("isisSysVersion", "one(1)")
+ assert r2_snmp.test_oid("isisSysLevelType", "level1and2(3)")
+ assert r2_snmp.test_oid("isisSysID", "00 00 00 00 00 02")
+ assert r2_snmp.test_oid("isisSysMaxPathSplits", "32")
+ assert r2_snmp.test_oid("isisSysMaxLSPGenInt", "900 seconds")
+ assert r2_snmp.test_oid("isisSysAdminState", "on(1)")
+ assert r2_snmp.test_oid("isisSysMaxAge", "1200 seconds")
+ assert r2_snmp.test_oid("isisSysProtSupported", "07 5 6 7")
+
+
+circtable_test = {
+ "isisCircAdminState": ["on(1)", "on(1)"],
+ "isisCircExistState": ["active(1)", "active(1)"],
+ "isisCircType": ["broadcast(1)", "ptToPt(2)"],
+ "isisCircExtDomain": ["false(2)", "false(2)"],
+ "isisCircLevelType": ["level1(1)", "level1(1)"],
+ "isisCircPassiveCircuit": ["false(2)", "false(2)"],
+ "isisCircMeshGroupEnabled": ["inactive(1)", "inactive(1)"],
+ "isisCircSmallHellos": ["false(2)", "false(2)"],
+ "isisCirc3WayEnabled": ["false(2)", "false(2)"],
+}
+
+
+def test_r1_isisCircTable():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(1, 1, 0))
+ oids.append(generate_oid(1, 2, 0))
+
+ # check items
+ for item in circtable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, circtable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, circtable_test[item], oids), assertmsg
+
+
+circleveltable_test = {
+ "isisCircLevelMetric": ["10", "10"],
+ "isisCircLevelWideMetric": ["10", "10"],
+ "isisCircLevelISPriority": ["64", "64"],
+ "isisCircLevelHelloMultiplier": ["10", "10"],
+ "isisCircLevelHelloTimer": [
+ "3000 milliseconds",
+ "3000 milliseconds",
+ ],
+ "isisCircLevelMinLSPRetransInt": [
+ "1 seconds",
+ "1 seconds",
+ ],
+}
+
+
+def test_r1_isislevelCircTable():
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(2, 1, "area"))
+ oids.append(generate_oid(2, 2, "area"))
+
+ # check items
+ for item in circleveltable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, circleveltable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, circleveltable_test[item], oids), assertmsg
+
+
+adjtable_test = {
+ "isisISAdjState": ["up(3)", "up(3)"],
+ "isisISAdj3WayState": ["down(2)", "up(0)"],
+ "isisISAdjNeighSysType": ["l1IntermediateSystem(1)", "l1IntermediateSystem(1)"],
+ "isisISAdjNeighSysID": ["00 00 00 00 00 04", "00 00 00 00 00 03"],
+ "isisISAdjUsage": ["0", "level1(1)"],
+ "isisISAdjNeighPriority": ["64", "0"],
+}
+
+adjtable_down_test = {
+ "isisISAdjState": ["up(3)"],
+ "isisISAdj3WayState": ["down(2)"],
+ "isisISAdjNeighSysType": ["l1IntermediateSystem(1)"],
+ "isisISAdjNeighSysID": ["00 00 00 00 00 04"],
+ "isisISAdjUsage": ["0"],
+ "isisISAdjNeighPriority": ["64"],
+}
+
+
+def test_r1_isisAdjTable():
+ "check ISIS Adjacency Table"
+ tgen = get_topogen()
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ oids = []
+ oids.append(generate_oid(2, 1, 1))
+ oids.append(generate_oid(2, 2, 1))
+
+ oids_down = []
+ oids_down.append(generate_oid(2, 1, 1))
+
+ # check items
+ for item in adjtable_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, adjtable_test[item], oids, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(item, adjtable_test[item], oids), assertmsg
+
+ # shutdown interface and one adjacency should be removed
+ "check ISIS adjacency is removed when interface is shutdown"
+ r1.vtysh_cmd("conf t\ninterface r1-eth1\nshutdown")
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ for item in adjtable_down_test.keys():
+ assertmsg = "{} should be {} oids {} full dict {}:".format(
+ item, adjtable_down_test[item], oids_down, r1_snmp.walk(item)
+ )
+ assert r1_snmp.test_oid_walk(
+ item, adjtable_down_test[item], oids_down
+ ), assertmsg
+
+ # no shutdown interface and adjacency should be restored
+ r1.vtysh_cmd("conf t\ninterface r1-eth1\nno shutdown")
+
+
+# Memory leak test template
+# disabling memory leak
+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/isis_sr_te_topo1/dst/zebra.conf b/tests/topotests/isis_sr_te_topo1/dst/zebra.conf
new file mode 100644
index 0000000..0c7937b
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/dst/zebra.conf
@@ -0,0 +1,19 @@
+log file zebra.log
+!
+hostname dst
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 9.9.9.2/32
+ ipv6 address 2001:db8:1066::2/128
+!
+interface eth-rt6
+ ip address 10.0.11.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf b/tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf
new file mode 100644
index 0000000..efc0370
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/bgpd.conf
@@ -0,0 +1,16 @@
+log file bgpd.log
+!
+router bgp 1
+ bgp router-id 1.1.1.1
+ neighbor 6.6.6.6 remote-as 1
+ neighbor 6.6.6.6 update-source lo
+ !
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 6.6.6.6 next-hop-self
+ neighbor 6.6.6.6 route-map SET_SR_POLICY in
+ exit-address-family
+!
+route-map SET_SR_POLICY permit 10
+ set sr-te color 1
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..3d5ac20
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/isisd.conf
@@ -0,0 +1,30 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+ segment-routing prefix 2001:db8:1000::1/128 index 11
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/pathd.conf b/tests/topotests/isis_sr_te_topo1/rt1/pathd.conf
new file mode 100644
index 0000000..9119714
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/pathd.conf
@@ -0,0 +1,21 @@
+log file pathd.log
+!
+hostname rt1
+!
+segment-routing
+ traffic-eng
+ segment-list default
+ index 10 mpls label 16050
+ index 20 mpls label 16060
+ !
+ segment-list test
+ index 10 mpls label 16020
+ index 20 mpls label 16040
+ index 30 mpls label 16060
+ !
+ policy color 1 endpoint 6.6.6.6
+ name default
+ binding-sid 1111
+ !
+ !
+! \ No newline at end of file
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_with_candidate.ref
new file mode 100644
index 0000000..d4b27d1
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_with_candidate.ref
@@ -0,0 +1,91 @@
+{
+ "1111":{
+ "inLabel":1111,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16050,
+ "outLabelStack":[
+ 16050,
+ 16060
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_without_candidate.ref
new file mode 100644
index 0000000..5fe58d0
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step1/show_mpls_table_without_candidate.ref
@@ -0,0 +1,74 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data.ref b/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data.ref
new file mode 100644
index 0000000..4ef8d94
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000..9b28f6a
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000..9b28f6a
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000..2491171
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref
@@ -0,0 +1,25 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": false
+ },
+ {
+ "preference": 200,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table.ref
new file mode 100644
index 0000000..21f71f1
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table.ref
@@ -0,0 +1,20 @@
+{
+ "1111":{
+ "inLabel":1111,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16020,
+ "outLabelStack":[
+ 16020,
+ 16040,
+ 16060
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_add_segment.ref b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_add_segment.ref
new file mode 100644
index 0000000..3635c89
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_add_segment.ref
@@ -0,0 +1,21 @@
+{
+ "1111":{
+ "inLabel":1111,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16020,
+ "outLabelStack":[
+ 16020,
+ 16040,
+ 16050,
+ 16060
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_change_segment.ref b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_change_segment.ref
new file mode 100644
index 0000000..5712d21
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step4/show_mpls_table_change_segment.ref
@@ -0,0 +1,21 @@
+{
+ "1111":{
+ "inLabel":1111,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16020,
+ "outLabelStack":[
+ 16020,
+ 16040,
+ 16030,
+ 16060
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_active_srte.ref b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_active_srte.ref
new file mode 100644
index 0000000..5a76246
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_active_srte.ref
@@ -0,0 +1,29 @@
+{
+ "9.9.9.2\/32":[
+ {
+ "prefix":"9.9.9.2\/32",
+ "protocol":"bgp",
+ "installed":true,
+ "nexthops":[
+ {
+ "ip":"6.6.6.6",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true,
+ "srteColor":1
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050,
+ 16060
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_empty.ref b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_empty.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_empty.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref
new file mode 100644
index 0000000..09d5958
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_ip_route_bgp_inactive_srte.ref
@@ -0,0 +1,38 @@
+{
+ "9.9.9.2\/32":[
+ {
+ "prefix":"9.9.9.2\/32",
+ "protocol":"bgp",
+ "installed":true,
+ "nexthops":[
+ {
+ "ip":"6.6.6.6",
+ "afi":"ipv4",
+ "active":true,
+ "recursive":true,
+ "srteColor":1
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_active.ref b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_active.ref
new file mode 100644
index 0000000..e26039b
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_active.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_inactive.ref b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_inactive.ref
new file mode 100644
index 0000000..01505c0
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/step5/show_operational_data_inactive.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": false,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..37b3f27
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt1/zebra.conf
@@ -0,0 +1,19 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..ba214c9
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt2/isisd.conf
@@ -0,0 +1,41 @@
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20 no-php-flag
+ segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..f9ac098
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt2/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..482d815
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt3/isisd.conf
@@ -0,0 +1,41 @@
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30 no-php-flag
+ segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..441c9a3
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt3/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+!
+interface eth-rt5-1
+ ip address 10.0.4.3/24
+!
+interface eth-rt5-2
+ ip address 10.0.5.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..851c6da
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt4/isisd.conf
@@ -0,0 +1,48 @@
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40 no-php-flag
+ segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt4/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..a2569aa
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt4/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt2-1
+ ip address 10.0.2.4/24
+!
+interface eth-rt2-2
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..4cc54f3
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt5/isisd.conf
@@ -0,0 +1,48 @@
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50 no-php-flag
+ segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt5/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..f62cc8f
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt5/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt3-1
+ ip address 10.0.4.5/24
+!
+interface eth-rt3-2
+ ip address 10.0.5.5/24
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf b/tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf
new file mode 100644
index 0000000..e72ee52
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/bgpd.conf
@@ -0,0 +1,12 @@
+log file bgpd.log
+!
+router bgp 1
+ bgp router-id 6.6.6.6
+ neighbor 1.1.1.1 remote-as 1
+ neighbor 1.1.1.1 update-source lo
+ !
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 1.1.1.1 next-hop-self
+ exit-address-family
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf b/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..8fec87b
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/isisd.conf
@@ -0,0 +1,36 @@
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60
+ segment-routing prefix 2001:db8:1000::6/128 index 61
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/pathd.conf b/tests/topotests/isis_sr_te_topo1/rt6/pathd.conf
new file mode 100644
index 0000000..3bada71
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/pathd.conf
@@ -0,0 +1,21 @@
+log file pathd.log
+!
+hostname rt6
+!
+segment-routing
+ traffic-eng
+ segment-list default
+ index 10 mpls label 16020
+ index 20 mpls label 16010
+ !
+ segment-list test
+ index 10 mpls label 16050
+ index 20 mpls label 16030
+ index 30 mpls label 16010
+ !
+ policy color 1 endpoint 1.1.1.1
+ name default
+ binding-sid 6666
+ !
+ !
+!
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_with_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_with_candidate.ref
new file mode 100644
index 0000000..2bb0003
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_with_candidate.ref
@@ -0,0 +1,91 @@
+{
+ "6666":{
+ "inLabel":6666,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16020,
+ "outLabelStack":[
+ 16020,
+ 16010
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16010": {
+ "inLabel": 16010,
+ "installed": true,
+ "nexthops": [
+ {
+ "distance": 150,
+ "installed": true,
+ "nexthop": "10.0.7.4",
+ "outLabel": 16010,
+ "type": "SR (IS-IS)"
+ },
+ {
+ "distance": 150,
+ "installed": true,
+ "nexthop": "10.0.8.5",
+ "outLabel": 16010,
+ "type": "SR (IS-IS)"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_without_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_without_candidate.ref
new file mode 100644
index 0000000..348f776
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step1/show_mpls_table_without_candidate.ref
@@ -0,0 +1,74 @@
+{
+ "16010": {
+ "inLabel": 16010,
+ "installed": true,
+ "nexthops": [
+ {
+ "distance": 150,
+ "installed": true,
+ "nexthop": "10.0.7.4",
+ "outLabel": 16010,
+ "type": "SR (IS-IS)"
+ },
+ {
+ "distance": 150,
+ "installed": true,
+ "nexthop": "10.0.8.5",
+ "outLabel": 16010,
+ "type": "SR (IS-IS)"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data.ref b/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data.ref
new file mode 100644
index 0000000..241c80b
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000..20ea69e
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref
@@ -0,0 +1,19 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000..20ea69e
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref
@@ -0,0 +1,19 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000..10cafe9
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref
@@ -0,0 +1,23 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": false
+ },
+ {
+ "preference": 200,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis_sr_te_topo1/rt6/step4/show_mpls_table.ref
new file mode 100644
index 0000000..95bf995
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/step4/show_mpls_table.ref
@@ -0,0 +1,20 @@
+{
+ "6666":{
+ "inLabel":6666,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR-TE",
+ "outLabel":16050,
+ "outLabelStack":[
+ 16050,
+ 16030,
+ 16010
+ ],
+ "distance":150,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_te_topo1/rt6/zebra.conf b/tests/topotests/isis_sr_te_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..f2f1a3e
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/rt6/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+!
+interface eth-dst
+ ip address 10.0.11.1/24
+!
+ip forwarding
+!
+ip route 9.9.9.2/32 10.0.11.2
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py b/tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py
new file mode 100755
index 0000000..b3b14b5
--- /dev/null
+++ b/tests/topotests/isis_sr_te_topo1/test_isis_sr_te_topo1.py
@@ -0,0 +1,870 @@
+#!/usr/bin/env python
+
+#
+# test_isis_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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_isis_sr_te_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
+ | | | |
+ 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
+ | | | |
+ eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+ |eth-dst (.1)
+ |
+ |10.0.11.0/24
+ |
+ |eth-rt6 (.2)
+ +---------+
+ | |
+ | DST |
+ | 9.9.9.2 |
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.pathd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+ switch = tgen.add_switch("s9")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
+ switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+
+ frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+ if not os.path.isfile(os.path.join(frrdir, "pathd")):
+ pytest.skip("pathd daemon wasn't built")
+
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ 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 setup_testcase(msg):
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+def print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def compare_json_test(router, command, reference, exact):
+ output = router.vtysh_cmd(command, isjson=True)
+ result = topotest.json_cmp(output, reference)
+
+ # Note: topotest.json_cmp() just checks on inclusion of keys.
+ # For exact matching also compare the other way around.
+ if not result and exact:
+ return topotest.json_cmp(reference, output)
+ else:
+ return result
+
+
+def cmp_json_output(rname, command, reference, exact=False):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_json_test, tgen.gears[rname], command, expected, exact)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def cmp_json_output_exact(rname, command, reference):
+ return cmp_json_output(rname, command, reference, True)
+
+
+def compare_json_test_inverted(router, command, reference, exact):
+ "logically inverts result of compare_json_test"
+
+ # None vs something else
+ result = compare_json_test(router, command, reference, exact)
+ if result is None:
+ return "Some"
+ return None
+
+
+def cmp_json_output_doesnt(rname, command, reference):
+ "Compare router JSON output, shouldn't include reference"
+
+ logger.info('Comparing (anti) router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(
+ compare_json_test_inverted, tgen.gears[rname], command, expected, exact=False
+ )
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def dump_json(v):
+ if isinstance(v, (dict, list)):
+ return "\t" + "\t".join(
+ json.dumps(v, indent=4, separators=(",", ": ")).splitlines(True)
+ )
+ else:
+ return "'{}'".format(v)
+
+
+def add_candidate_path(rname, endpoint, pref, name, segment_list="default", color=1):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color """
+ + str(color)
+ + " endpoint "
+ + endpoint
+ + """" \
+ -c "candidate-path preference """
+ + str(pref)
+ + """ name """
+ + name
+ + """ explicit segment-list """
+ + segment_list
+ + '''"'''
+ )
+
+
+def delete_candidate_path(rname, endpoint, pref, color=1):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color """
+ + str(color)
+ + " endpoint "
+ + endpoint
+ + """" \
+ -c "no candidate-path preference """
+ + str(pref)
+ + '''"'''
+ )
+
+
+def add_segment(rname, name, index, label):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "segment-list """
+ + name
+ + """" \
+ -c "index """
+ + str(index)
+ + """ mpls label """
+ + str(label)
+ + '''"'''
+ )
+
+
+def delete_segment(rname, name, index):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "segment-list """
+ + name
+ + """" \
+ -c "no index """
+ + str(index)
+ + '''"'''
+ )
+
+
+def create_sr_policy(rname, endpoint, bsid):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color 1 endpoint """
+ + endpoint
+ + """" \
+ -c "name default" \
+ -c "binding-sid """
+ + str(bsid)
+ + '''"'''
+ )
+
+
+def delete_sr_policy(rname, endpoint):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "no policy color 1 endpoint """
+ + endpoint
+ + '''"'''
+ )
+
+
+def create_prefix_sid(rname, prefix, sid):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "router isis 1" \
+ -c "segment-routing prefix """
+ + prefix
+ + " index "
+ + str(sid)
+ + '''"'''
+ )
+
+
+def delete_prefix_sid(rname, prefix):
+ get_topogen().net[rname].cmd(
+ ''' \
+ vtysh -c "conf t" \
+ -c "router isis 1" \
+ -c "no segment-routing prefix "'''
+ + prefix
+ )
+
+
+def set_route_map_color(rname, color):
+ get_topogen().net[rname].cmd(
+ ''' \
+ vtysh -c "conf t" \
+ -c "route-map SET_SR_POLICY permit 10" \
+ -c "set sr-te color "'''
+ + str(color)
+ )
+
+
+def router_bgp_shutdown_neighbor(rname, neighbor):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "router bgp 1" \
+ -c " neighbor """
+ + neighbor
+ + ' shutdown"'
+ )
+
+
+def router_bgp_no_shutdown_neighbor(rname, neighbor):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "router bgp 1" \
+ -c " no neighbor """
+ + neighbor
+ + ' shutdown"'
+ )
+
+
+def show_running_cfg(rname):
+ output = (
+ get_topogen()
+ .net[rname]
+ .cmd(
+ """ \
+ vtysh -c "show run" """
+ )
+ )
+ logger.info(output)
+
+
+#
+# Step 1
+#
+# Checking the MPLS table using a single SR Policy and a single Candidate Path
+#
+def test_srte_init_step1():
+ setup_testcase("Test (step 1): wait for IS-IS convergence / label distribution")
+
+ for rname in ["rt1", "rt6"]:
+ cmp_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref"
+ )
+
+
+def test_srte_add_candidate_check_mpls_table_step1():
+ setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ cmp_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref"
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+def test_srte_reinstall_sr_policy_check_mpls_table_step1():
+ setup_testcase(
+ "Test (step 1): check MPLS table after the SR Policy was removed and reinstalled"
+ )
+
+ for rname, endpoint, bsid in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ delete_sr_policy(rname, endpoint)
+ cmp_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table_without_candidate.ref"
+ )
+ create_sr_policy(rname, endpoint, bsid)
+ add_candidate_path(rname, endpoint, 100, "default")
+ cmp_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table_with_candidate.ref"
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+#
+# Step 2
+#
+# Checking pathd operational data using a single SR Policy and a single Candidate Path
+#
+def test_srte_bare_policy_step2():
+ setup_testcase("Test (step 2): bare SR Policy should not be operational")
+
+ for rname in ["rt1", "rt6"]:
+ cmp_json_output_exact(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data.ref",
+ )
+
+
+def test_srte_add_candidate_check_operational_data_step2():
+ setup_testcase(
+ "Test (step 2): add single Candidate Path, SR Policy should be operational"
+ )
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data_with_candidate.ref",
+ )
+
+
+def test_srte_config_remove_candidate_check_operational_data_step2():
+ setup_testcase(
+ "Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore"
+ )
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ delete_candidate_path(rname, endpoint, 100)
+ cmp_json_output_exact(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data.ref",
+ )
+
+
+#
+# Step 3
+#
+# Testing the Candidate Path selection
+#
+def test_srte_add_two_candidates_step3():
+ setup_testcase("Test (step 3): second Candidate Path has higher Priority")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("100", "first"), ("200", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_two_candidates.ref",
+ )
+
+ # cleanup
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref in ["100", "200"]:
+ delete_candidate_path(rname, endpoint, pref)
+
+
+def test_srte_add_two_candidates_with_reverse_priority_step3():
+ setup_testcase("Test (step 3): second Candidate Path has lower Priority")
+
+ # Use reversed priorities here
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("200", "first"), ("100", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_two_candidates.ref",
+ )
+
+ # cleanup
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref in ["100", "200"]:
+ delete_candidate_path(rname, endpoint, pref)
+
+
+def test_srte_remove_best_candidate_step3():
+ setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("100", "first"), ("200", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+
+ # Delete candidate with higher priority
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ delete_candidate_path(rname, endpoint, 200)
+
+ # Candidate with lower priority should get active now
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_single_candidate.ref",
+ )
+ # cleanup
+ delete_candidate_path(rname, endpoint, 100)
+
+
+#
+# Step 4
+#
+# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
+#
+def test_srte_change_segment_list_check_mpls_table_step4():
+ setup_testcase("Test (step 4): check MPLS table for changed Segment List")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ # now change the segment list name
+ add_candidate_path(rname, endpoint, 100, "default", "test")
+ cmp_json_output(rname, "show mpls table json", "step4/show_mpls_table.ref")
+ delete_candidate_path(rname, endpoint, 100)
+
+
+def test_srte_segment_list_add_segment_check_mpls_table_step4():
+ setup_testcase(
+ "Test (step 4): check MPLS table for added (then changed and finally deleted) segment"
+ )
+
+ add_candidate_path("rt1", "6.6.6.6", 100, "default", "test")
+
+ # first add a new segment
+ add_segment("rt1", "test", 25, 16050)
+ cmp_json_output(
+ "rt1", "show mpls table json", "step4/show_mpls_table_add_segment.ref"
+ )
+
+ # ... then change it ...
+ add_segment("rt1", "test", 25, 16030)
+ cmp_json_output(
+ "rt1", "show mpls table json", "step4/show_mpls_table_change_segment.ref"
+ )
+
+ # ... and finally delete it
+ delete_segment("rt1", "test", 25)
+ cmp_json_output("rt1", "show mpls table json", "step4/show_mpls_table.ref")
+ delete_candidate_path("rt1", "6.6.6.6", 100)
+
+
+def save_rt(routername, filename):
+ save_filename = routername + "/" + filename
+ tgen = get_topogen()
+ router = tgen.gears[routername]
+
+ config_output = router.vtysh_cmd("sh run")
+
+ route_output_json = json.loads(router.vtysh_cmd("show ip route bgp json"))
+ route_output = dump_json(route_output_json)
+
+ f = open(save_filename, "w")
+ f.write(config_output)
+ f.write(route_output)
+ f.close()
+
+
+#
+# Step 5
+#
+# Checking the nexthop using a single SR Policy and a Candidate Path with configured route-map
+#
+def test_srte_route_map_with_sr_policy_check_nextop_step5():
+ setup_testcase(
+ "Test (step 5): recursive nexthop learned through BGP neighbour should be aligned with SR Policy from route-map"
+ )
+
+ # (re-)build the SR Policy two times to ensure that reinstalling still works
+ for i in [1, 2]:
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
+ )
+
+ delete_sr_policy("rt1", "6.6.6.6")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
+ )
+
+ create_sr_policy("rt1", "6.6.6.6", 1111)
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
+ )
+
+ add_candidate_path("rt1", "6.6.6.6", 100, "default")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ delete_candidate_path("rt1", "6.6.6.6", 100)
+
+
+def test_srte_route_map_sr_policy_vs_route_order_step5():
+ setup_testcase(
+ "Test (step 5): Config policy first, add route after and check route validity"
+ )
+
+ #
+ # BGP route and route-map are already configured.
+ # route-map sets color 1 on BGP routes
+
+ # Developer: to force pause here
+ # tgen = get_topogen()
+ # tgen.mininet_cli()
+
+ #
+ # Configure policy/path
+ #
+ add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+ #
+ # Route should be valid
+ #
+ logger.info(
+ "BGP route and route-map are already configured, SR candidate path added after. Route should be valid"
+ )
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ #
+ # shutdown/no-shutdown on BGP neighbor to delete/re-add BGP route
+ #
+ router_bgp_shutdown_neighbor("rt1", "6.6.6.6")
+ router_bgp_no_shutdown_neighbor("rt1", "6.6.6.6")
+
+ #
+ # Route should be valid (but isn't)
+ #
+ logger.info(
+ "After shutdown + no-shutdown neighbor. Route should be valid, but isn't"
+ )
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ #
+ # delete and re-add policy/path
+ #
+ delete_candidate_path("rt1", "6.6.6.6", 100, 1)
+ add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+ #
+ # Route should be valid
+ #
+ logger.info("After re-add candidate path. Route should be valid")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ # Developer: to force pause here
+ # tgen = get_topogen()
+ # tgen.mininet_cli()
+
+ # clean up
+ delete_candidate_path("rt1", "6.6.6.6", 100, 2)
+
+
+def test_srte_route_map_sr_policy_vs_routemap_order_step5():
+ setup_testcase(
+ "Test (step 5): Config policy first, set route-map after and check route validity"
+ )
+
+ #
+ # BGP route and route-map are already configured.
+ # route-map sets color 1 on BGP routes
+ #
+
+ #
+ # Configure policy/path
+ #
+ add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+ #
+ # Route should be valid
+ #
+ logger.info("After add candidate path. Route should be valid")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ # Developer: to force pause here
+ # tgen = get_topogen()
+ # tgen.mininet_cli()
+
+ #
+ # change route-map color to someting else and back again
+ #
+ set_route_map_color("rt1", 2)
+ logger.info("route-map color was set to 2")
+ # show_running_cfg("rt1")
+ # 220625 nexthop no longer becomes empty. Colored routes without
+ # matching SR policies now fall back to their non-colored equivalent
+ # nexthops. So the route to 9.9.9.9/32 will now be valid, but with
+ # different nexthop values.
+ logger.info("now route table will lose policy-mapped route")
+ cmp_json_output_doesnt(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+ set_route_map_color("rt1", 1)
+ logger.info("route-map color was set to 1")
+ # show_running_cfg("rt1")
+
+ #
+ # Route should be valid (but isn't)
+ #
+ logger.info("After change route-map color. Route should be valid, but isn't")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ #
+ # delete and re-add policy/path
+ #
+ delete_candidate_path("rt1", "6.6.6.6", 100, 1)
+ add_candidate_path("rt1", "6.6.6.6", 100, "default", "default", 1)
+
+ #
+ # Route should be valid
+ #
+ logger.info("After delete/re-add candidate path. Route should be valid")
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+ # Developer: force pause
+ # tgen = get_topogen()
+ # tgen.mininet_cli()
+
+ # clean up
+ delete_candidate_path("rt1", "6.6.6.6", 100, 2)
+
+
+def test_srte_route_map_with_sr_policy_reinstall_prefix_sid_check_nextop_step5():
+ setup_testcase(
+ "Test (step 5): remove and re-install prefix SID on fist path element and check SR Policy activity"
+ )
+
+ # first add a candidate path so the SR Policy is active
+ add_candidate_path("rt1", "6.6.6.6", 100, "default")
+ cmp_json_output(
+ "rt1",
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step5/show_operational_data_active.ref",
+ )
+
+ # delete prefix SID from first element of the configured path and check
+ # if the SR Policy is inactive since the label can't be resolved anymore
+ delete_prefix_sid("rt5", "5.5.5.5/32")
+ cmp_json_output(
+ "rt1",
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step5/show_operational_data_inactive.ref",
+ )
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_inactive_srte.ref"
+ )
+
+ # re-create the prefix SID and check if the SR Policy is active
+ create_prefix_sid("rt5", "5.5.5.5/32", 50)
+ cmp_json_output(
+ "rt1",
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step5/show_operational_data_active.ref",
+ )
+ cmp_json_output(
+ "rt1", "show ip route bgp json", "step5/show_ip_route_bgp_active_srte.ref"
+ )
+
+
+# Memory leak test template
+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/isis_sr_topo1/__init__.py b/tests/topotests/isis_sr_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/__init__.py
diff --git a/tests/topotests/isis_sr_topo1/rt1/isisd.conf b/tests/topotests/isis_sr_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..ed09753
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/isisd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+ segment-routing prefix 2001:db8:1000::1/128 index 11
+!
diff --git a/tests/topotests/isis_sr_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..53bf8cb
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,327 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step1/show_mpls_table.ref
new file mode 100644
index 0000000..5b1950d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step1/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..26f0dff
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,32 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step10/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step10/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step10/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step10/show_mpls_table.ref
new file mode 100644
index 0000000..7e6c726
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step10/show_mpls_table.ref
@@ -0,0 +1,192 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step2/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step2/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step2/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step2/show_mpls_table.ref
new file mode 100644
index 0000000..5b1950d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step2/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step3/show_ip_route.ref
new file mode 100644
index 0000000..71f9ebd
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step3/show_ip_route.ref
@@ -0,0 +1,287 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..304c0a4
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step3/show_ipv6_route.ref
@@ -0,0 +1,121 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step3/show_mpls_table.ref
new file mode 100644
index 0000000..94b3cb6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step3/show_mpls_table.ref
@@ -0,0 +1,134 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step4/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step4/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step4/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step4/show_mpls_table.ref
new file mode 100644
index 0000000..6500a47
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step4/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step5/show_ip_route.ref
new file mode 100644
index 0000000..16d9358
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step5/show_ip_route.ref
@@ -0,0 +1,314 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..f2093a3
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step5/show_ipv6_route.ref
@@ -0,0 +1,146 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step5/show_mpls_table.ref
new file mode 100644
index 0000000..94b3cb6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step5/show_mpls_table.ref
@@ -0,0 +1,134 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step6/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step6/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step6/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step6/show_mpls_table.ref
new file mode 100644
index 0000000..5b1950d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step6/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step7/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step7/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step7/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step7/show_mpls_table.ref
new file mode 100644
index 0000000..5b1950d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step7/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step8/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step8/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step8/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step8/show_mpls_table.ref
new file mode 100644
index 0000000..5b1950d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step8/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt1/step9/show_ip_route.ref
new file mode 100644
index 0000000..c712538
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step9/show_ip_route.ref
@@ -0,0 +1,320 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt1/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..0b39584
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step9/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt1/step9/show_mpls_table.ref
new file mode 100644
index 0000000..7e6c726
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step9/show_mpls_table.ref
@@ -0,0 +1,192 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17101,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt1/zebra.conf b/tests/topotests/isis_sr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..37b3f27
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt1/zebra.conf
@@ -0,0 +1,19 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/rt2/isisd.conf b/tests/topotests/isis_sr_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..9b8efe4
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0002.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20 no-php-flag
+ segment-routing prefix 2001:db8:1000::2/128 index 21 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000..109b94f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step1/show_ip_route.ref
@@ -0,0 +1,380 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..eae700e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,179 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step1/show_mpls_table.ref
new file mode 100644
index 0000000..a32cd1d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step1/show_mpls_table.ref
@@ -0,0 +1,228 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..07f43e5
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,70 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step10/show_ip_route.ref
new file mode 100644
index 0000000..b7d52ce
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step10/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..355436c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step10/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step10/show_mpls_table.ref
new file mode 100644
index 0000000..4cbdb9f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step10/show_mpls_table.ref
@@ -0,0 +1,186 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step2/show_ip_route.ref
new file mode 100644
index 0000000..159392f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step2/show_ip_route.ref
@@ -0,0 +1,353 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f6384
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step2/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step2/show_mpls_table.ref
new file mode 100644
index 0000000..0692553
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step2/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step3/show_ip_route.ref
new file mode 100644
index 0000000..16f49ff
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step3/show_ip_route.ref
@@ -0,0 +1,306 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..bde83c3
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step3/show_ipv6_route.ref
@@ -0,0 +1,130 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step3/show_mpls_table.ref
new file mode 100644
index 0000000..cbb0d5c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step3/show_mpls_table.ref
@@ -0,0 +1,168 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step4/show_ip_route.ref
new file mode 100644
index 0000000..159392f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step4/show_ip_route.ref
@@ -0,0 +1,353 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f6384
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step4/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step4/show_mpls_table.ref
new file mode 100644
index 0000000..0692553
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step4/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step5/show_ip_route.ref
new file mode 100644
index 0000000..fbfcce1
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step5/show_ip_route.ref
@@ -0,0 +1,347 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..f747a96
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step5/show_ipv6_route.ref
@@ -0,0 +1,155 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step5/show_mpls_table.ref
new file mode 100644
index 0000000..cbb0d5c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step5/show_mpls_table.ref
@@ -0,0 +1,168 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step6/show_ip_route.ref
new file mode 100644
index 0000000..159392f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step6/show_ip_route.ref
@@ -0,0 +1,353 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f6384
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step6/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step6/show_mpls_table.ref
new file mode 100644
index 0000000..0692553
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step6/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step7/show_ip_route.ref
new file mode 100644
index 0000000..09ab6d4
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step7/show_ip_route.ref
@@ -0,0 +1,350 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..851275f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step7/show_ipv6_route.ref
@@ -0,0 +1,158 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step7/show_mpls_table.ref
new file mode 100644
index 0000000..87946aa
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step7/show_mpls_table.ref
@@ -0,0 +1,180 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step8/show_ip_route.ref
new file mode 100644
index 0000000..159392f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step8/show_ip_route.ref
@@ -0,0 +1,353 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f6384
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step8/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step8/show_mpls_table.ref
new file mode 100644
index 0000000..0692553
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step8/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt2/step9/show_ip_route.ref
new file mode 100644
index 0000000..fc82ada
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step9/show_ip_route.ref
@@ -0,0 +1,353 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt2/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..355436c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step9/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt2/step9/show_mpls_table.ref
new file mode 100644
index 0000000..0520172
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step9/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt2/zebra.conf b/tests/topotests/isis_sr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..f9ac098
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt2/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/rt3/isisd.conf b/tests/topotests/isis_sr_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..ded8bd1
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/isisd.conf
@@ -0,0 +1,42 @@
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0003.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 17000 24999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30 no-php-flag
+ segment-routing prefix 2001:db8:1000::3/128 index 31 no-php-flag
+!
diff --git a/tests/topotests/isis_sr_topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000..241f768
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step1/show_ip_route.ref
@@ -0,0 +1,380 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..dd78c7d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,179 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step1/show_mpls_table.ref
new file mode 100644
index 0000000..8c6fca7
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step1/show_mpls_table.ref
@@ -0,0 +1,228 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..7fa6f5c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,70 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt5-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step10/show_ip_route.ref
new file mode 100644
index 0000000..40a98ab
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step10/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..1fb5040
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step10/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step10/show_mpls_table.ref
new file mode 100644
index 0000000..44ddc4b
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step10/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step2/show_ip_route.ref
new file mode 100644
index 0000000..55d8213
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step2/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..4f6441e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step2/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step2/show_mpls_table.ref
new file mode 100644
index 0000000..db8253f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step2/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step3/show_ip_route.ref
new file mode 100644
index 0000000..ed5cef8
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step3/show_ip_route.ref
@@ -0,0 +1,313 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..b33058c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step3/show_ipv6_route.ref
@@ -0,0 +1,130 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step3/show_mpls_table.ref
new file mode 100644
index 0000000..70cccc0
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step3/show_mpls_table.ref
@@ -0,0 +1,168 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step4/show_ip_route.ref
new file mode 100644
index 0000000..55d8213
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step4/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..4f6441e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step4/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step4/show_mpls_table.ref
new file mode 100644
index 0000000..db8253f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step4/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step5/show_ip_route.ref
new file mode 100644
index 0000000..3adcdce
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step5/show_ip_route.ref
@@ -0,0 +1,354 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..863e26c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step5/show_ipv6_route.ref
@@ -0,0 +1,155 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step5/show_mpls_table.ref
new file mode 100644
index 0000000..70cccc0
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step5/show_mpls_table.ref
@@ -0,0 +1,168 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step6/show_ip_route.ref
new file mode 100644
index 0000000..55d8213
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step6/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..4f6441e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step6/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step6/show_mpls_table.ref
new file mode 100644
index 0000000..db8253f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step6/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step7/show_ip_route.ref
new file mode 100644
index 0000000..7f6e05f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step7/show_ip_route.ref
@@ -0,0 +1,357 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..f4770e2
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step7/show_ipv6_route.ref
@@ -0,0 +1,158 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step7/show_mpls_table.ref
new file mode 100644
index 0000000..cb49505
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step7/show_mpls_table.ref
@@ -0,0 +1,180 @@
+{
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step8/show_ip_route.ref
new file mode 100644
index 0000000..55d8213
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step8/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..4f6441e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step8/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step8/show_mpls_table.ref
new file mode 100644
index 0000000..db8253f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step8/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt3/step9/show_ip_route.ref
new file mode 100644
index 0000000..40a98ab
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step9/show_ip_route.ref
@@ -0,0 +1,360 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt3/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..1fb5040
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step9/show_ipv6_route.ref
@@ -0,0 +1,161 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt3/step9/show_mpls_table.ref
new file mode 100644
index 0000000..44ddc4b
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step9/show_mpls_table.ref
@@ -0,0 +1,204 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17011":{
+ "inLabel":17011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17021":{
+ "inLabel":17021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17031":{
+ "inLabel":17031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17041":{
+ "inLabel":17041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17051":{
+ "inLabel":17051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17061":{
+ "inLabel":17061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ },
+ "17100":{
+ "inLabel":17100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17101":{
+ "inLabel":17101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt3/zebra.conf b/tests/topotests/isis_sr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..441c9a3
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt3/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+!
+interface eth-rt5-1
+ ip address 10.0.4.3/24
+!
+interface eth-rt5-2
+ ip address 10.0.5.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/rt4/isisd.conf b/tests/topotests/isis_sr_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..ba97045
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/isisd.conf
@@ -0,0 +1,51 @@
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0004.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40 no-php-flag
+ segment-routing prefix 10.10.10.10/32 index 100 no-php-flag n-flag-clear
+ segment-routing prefix 2001:db8:1000::4/128 index 41 no-php-flag
+ segment-routing prefix 2001:db8:1000::10/128 index 101 no-php-flag n-flag-clear
+!
diff --git a/tests/topotests/isis_sr_topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000..493f3ab
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step1/show_ip_route.ref
@@ -0,0 +1,342 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..217a4a5
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,148 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step1/show_mpls_table.ref
new file mode 100644
index 0000000..3074039
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step1/show_mpls_table.ref
@@ -0,0 +1,215 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..2eb64b6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,82 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step10/show_ip_route.ref
new file mode 100644
index 0000000..11bc948
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step10/show_ip_route.ref
@@ -0,0 +1,297 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..844f6be
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step10/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step10/show_mpls_table.ref
new file mode 100644
index 0000000..f275056
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step10/show_mpls_table.ref
@@ -0,0 +1,185 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step2/show_ip_route.ref
new file mode 100644
index 0000000..c2fbdeb
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step2/show_ip_route.ref
@@ -0,0 +1,355 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..7f823b6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step2/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step2/show_mpls_table.ref
new file mode 100644
index 0000000..8dd3788
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step2/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..be1e00b
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step3/show_ip_route.ref
new file mode 100644
index 0000000..f2a54bf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step3/show_ip_route.ref
@@ -0,0 +1,336 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":40,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..34afda1
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step3/show_ipv6_route.ref
@@ -0,0 +1,126 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step3/show_mpls_table.ref
new file mode 100644
index 0000000..65336d8
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step3/show_mpls_table.ref
@@ -0,0 +1,197 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..bcade1c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step4/show_ip_route.ref
new file mode 100644
index 0000000..e930657
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step4/show_ip_route.ref
@@ -0,0 +1,355 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..ca61c6e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step4/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step4/show_mpls_table.ref
new file mode 100644
index 0000000..eb95fa9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step4/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step5/show_ip_route.ref
new file mode 100644
index 0000000..8b0ddd4
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step5/show_ip_route.ref
@@ -0,0 +1,347 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..94e1fac
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step5/show_ipv6_route.ref
@@ -0,0 +1,133 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step5/show_mpls_table.ref
new file mode 100644
index 0000000..cd47cfa
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step5/show_mpls_table.ref
@@ -0,0 +1,143 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step6/show_ip_route.ref
new file mode 100644
index 0000000..e930657
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step6/show_ip_route.ref
@@ -0,0 +1,355 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..ca61c6e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step6/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step6/show_mpls_table.ref
new file mode 100644
index 0000000..eb95fa9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step6/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step7/show_ip_route.ref
new file mode 100644
index 0000000..f5ac455
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step7/show_ip_route.ref
@@ -0,0 +1,349 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..1599c88
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step7/show_ipv6_route.ref
@@ -0,0 +1,133 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step7/show_mpls_table.ref
new file mode 100644
index 0000000..19b0beb
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step7/show_mpls_table.ref
@@ -0,0 +1,167 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step8/show_ip_route.ref
new file mode 100644
index 0000000..e930657
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step8/show_ip_route.ref
@@ -0,0 +1,355 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..ca61c6e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step8/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step8/show_mpls_table.ref
new file mode 100644
index 0000000..eb95fa9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step8/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt4/step9/show_ip_route.ref
new file mode 100644
index 0000000..a2b939a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step9/show_ip_route.ref
@@ -0,0 +1,355 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt4/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..844f6be
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step9/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt4/step9/show_mpls_table.ref
new file mode 100644
index 0000000..5805bf3
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step9/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18051,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..761cb2f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step2/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt4/zebra.conf b/tests/topotests/isis_sr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..b66a9af
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt4/zebra.conf
@@ -0,0 +1,30 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ip address 10.10.10.10/32
+ ipv6 address 2001:db8:1000::4/128
+ ipv6 address 2001:db8:1000::10/128
+!
+interface eth-rt2-1
+ ip address 10.0.2.4/24
+!
+interface eth-rt2-2
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/rt5/isisd.conf b/tests/topotests/isis_sr_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..3fe7bdf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/isisd.conf
@@ -0,0 +1,51 @@
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0005.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50 no-php-flag
+ segment-routing prefix 10.10.10.10/32 index 100 no-php-flag n-flag-clear
+ segment-routing prefix 2001:db8:1000::5/128 index 51 no-php-flag
+ segment-routing prefix 2001:db8:1000::10/128 index 101 no-php-flag n-flag-clear
+!
diff --git a/tests/topotests/isis_sr_topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000..0497bd8
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,342 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..294567e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,148 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step1/show_mpls_table.ref
new file mode 100644
index 0000000..99d1f77
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step1/show_mpls_table.ref
@@ -0,0 +1,215 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..1ff8c2c
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,82 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step10/show_ip_route.ref
new file mode 100644
index 0000000..29f4782
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step10/show_ip_route.ref
@@ -0,0 +1,331 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..c02d3cf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step10/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step10/show_mpls_table.ref
new file mode 100644
index 0000000..7cfea2a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step10/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step2/show_ip_route.ref
new file mode 100644
index 0000000..dc61b86
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step2/show_ip_route.ref
@@ -0,0 +1,338 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..fa42631
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step2/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step2/show_mpls_table.ref
new file mode 100644
index 0000000..08f1635
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step2/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d9ac0a8
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step3/show_ip_route.ref
new file mode 100644
index 0000000..2d983c4
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step3/show_ip_route.ref
@@ -0,0 +1,312 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..a3e705f
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step3/show_ipv6_route.ref
@@ -0,0 +1,126 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17041
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step3/show_mpls_table.ref
new file mode 100644
index 0000000..9980058
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step3/show_mpls_table.ref
@@ -0,0 +1,197 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17040,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17040,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17041,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17041,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17100,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0b8e6ba
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step4/show_ip_route.ref
new file mode 100644
index 0000000..0a64db6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step4/show_ip_route.ref
@@ -0,0 +1,323 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f8fe2
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step4/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step4/show_mpls_table.ref
new file mode 100644
index 0000000..a84ed90
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step4/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d9ac0a8
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,63 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step5/show_ip_route.ref
new file mode 100644
index 0000000..8848547
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step5/show_ip_route.ref
@@ -0,0 +1,315 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..60c6450
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step5/show_ipv6_route.ref
@@ -0,0 +1,133 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step5/show_mpls_table.ref
new file mode 100644
index 0000000..36c21b0
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step5/show_mpls_table.ref
@@ -0,0 +1,143 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step6/show_ip_route.ref
new file mode 100644
index 0000000..0a64db6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step6/show_ip_route.ref
@@ -0,0 +1,323 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f8fe2
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step6/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step6/show_mpls_table.ref
new file mode 100644
index 0000000..a84ed90
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step6/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step7/show_ip_route.ref
new file mode 100644
index 0000000..769bc4d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step7/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..e05cc1e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step7/show_ipv6_route.ref
@@ -0,0 +1,133 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step7/show_mpls_table.ref
new file mode 100644
index 0000000..c98da7e
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step7/show_mpls_table.ref
@@ -0,0 +1,167 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step8/show_ip_route.ref
new file mode 100644
index 0000000..0a64db6
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step8/show_ip_route.ref
@@ -0,0 +1,323 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..e9f8fe2
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step8/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 2
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step8/show_mpls_table.ref
new file mode 100644
index 0000000..a84ed90
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step8/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":2,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt5/step9/show_ip_route.ref
new file mode 100644
index 0000000..34cbf68
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step9/show_ip_route.ref
@@ -0,0 +1,323 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "distance":115,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt5/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..c02d3cf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step9/show_ipv6_route.ref
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt5/step9/show_mpls_table.ref
new file mode 100644
index 0000000..7cfea2a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step9/show_mpls_table.ref
@@ -0,0 +1,203 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17011,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":17031,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18041,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":18100,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..7cdaabf
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step4/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt5/zebra.conf b/tests/topotests/isis_sr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..90ceb81
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt5/zebra.conf
@@ -0,0 +1,30 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ip address 10.10.10.10/32
+ ipv6 address 2001:db8:1000::5/128
+ ipv6 address 2001:db8:1000::10/128
+!
+interface eth-rt3-1
+ ip address 10.0.4.5/24
+!
+interface eth-rt3-2
+ ip address 10.0.5.5/24
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/rt6/isisd.conf b/tests/topotests/isis_sr_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..e7a7e2d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/isisd.conf
@@ -0,0 +1,37 @@
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis 1
+ lsp-gen-interval 2
+ net 49.0000.0000.0000.0006.00
+ is-type level-1
+ topology ipv6-unicast
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60 explicit-null
+ segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null
+!
diff --git a/tests/topotests/isis_sr_topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000..7b62b0a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step1/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step1/show_mpls_table.ref
new file mode 100644
index 0000000..2c526e7
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step1/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..7348323
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step10/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step10/show_ip_route.ref
new file mode 100644
index 0000000..d430ef5
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step10/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step10/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step10/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step10/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step10/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step10/show_mpls_table.ref
new file mode 100644
index 0000000..be87ed9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step10/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18011":{
+ "inLabel":18011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step10/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step2/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step2/show_ip_route.ref
new file mode 100644
index 0000000..4b204db
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step2/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step2/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step2/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step2/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step2/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step2/show_mpls_table.ref
new file mode 100644
index 0000000..2c526e7
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step2/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16100":{
+ "inLabel":16100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16101":{
+ "inLabel":16101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step3/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step3/show_ip_route.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step3/show_ip_route.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step3/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step3/show_ipv6_route.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step3/show_ipv6_route.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step3/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step3/show_mpls_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step3/show_mpls_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0db3279
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,3 @@
+{
+
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step4/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step4/show_ip_route.ref
new file mode 100644
index 0000000..4b204db
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step4/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step4/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step4/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step4/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step4/show_mpls_table.ref
new file mode 100644
index 0000000..be87ed9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step4/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18011":{
+ "inLabel":18011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step4/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref
new file mode 100644
index 0000000..3ccd57a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_ip_route.ref
@@ -0,0 +1,293 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref
new file mode 100644
index 0000000..22d3f2a
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_ipv6_route.ref
@@ -0,0 +1,128 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_mpls_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step5/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step6/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step6/show_ip_route.ref
new file mode 100644
index 0000000..4b204db
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step6/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step6/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step6/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step6/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step6/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step6/show_mpls_table.ref
new file mode 100644
index 0000000..be87ed9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step6/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18011":{
+ "inLabel":18011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step6/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step7/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step7/show_ip_route.ref
new file mode 100644
index 0000000..1787988
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step7/show_ip_route.ref
@@ -0,0 +1,311 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step7/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step7/show_ipv6_route.ref
new file mode 100644
index 0000000..367d0ed
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step7/show_ipv6_route.ref
@@ -0,0 +1,146 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step7/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step7/show_mpls_table.ref
new file mode 100644
index 0000000..b44dda2
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step7/show_mpls_table.ref
@@ -0,0 +1,134 @@
+{
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step7/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step8/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step8/show_ip_route.ref
new file mode 100644
index 0000000..4b204db
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step8/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step8/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step8/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step8/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step8/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step8/show_mpls_table.ref
new file mode 100644
index 0000000..be87ed9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step8/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18011":{
+ "inLabel":18011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step8/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/step9/show_ip_route.ref b/tests/topotests/isis_sr_topo1/rt6/step9/show_ip_route.ref
new file mode 100644
index 0000000..4b204db
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step9/show_ip_route.ref
@@ -0,0 +1,317 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.10.10.10\/32":[
+ {
+ "prefix":"10.10.10.10\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16100
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step9/show_ipv6_route.ref b/tests/topotests/isis_sr_topo1/rt6/step9/show_ipv6_route.ref
new file mode 100644
index 0000000..834cdfe
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step9/show_ipv6_route.ref
@@ -0,0 +1,152 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::10\/128":[
+ {
+ "prefix":"2001:db8:1000::10\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16101
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step9/show_mpls_table.ref b/tests/topotests/isis_sr_topo1/rt6/step9/show_mpls_table.ref
new file mode 100644
index 0000000..be87ed9
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step9/show_mpls_table.ref
@@ -0,0 +1,170 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18011":{
+ "inLabel":18011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18021":{
+ "inLabel":18021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18031":{
+ "inLabel":18031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18041":{
+ "inLabel":18041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18051":{
+ "inLabel":18051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "18100":{
+ "inLabel":18100,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16100,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18101":{
+ "inLabel":18101,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16101,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_sr_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_sr_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref
new file mode 120000
index 0000000..0879b84
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/step9/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1 @@
+../step1/show_yang_interface_isis_adjacencies.ref \ No newline at end of file
diff --git a/tests/topotests/isis_sr_topo1/rt6/zebra.conf b/tests/topotests/isis_sr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..4d51d3d
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py b/tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py
new file mode 100644
index 0000000..40a7b76
--- /dev/null
+++ b/tests/topotests/isis_sr_topo1/test_isis_sr_topo1.py
@@ -0,0 +1,1086 @@
+#!/usr/bin/env python
+
+#
+# test_isis_sr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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_isis_sr_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
+ | | | |
+ 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
+ | | | |
+ eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import re
+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.
+
+pytestmark = [pytest.mark.isisd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step1/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step1/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step1/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step1():
+ logger.info("Test (step 1): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table.ref"
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable IS-IS on the eth-rt5 interface on rt4
+#
+# Expected changes:
+# -rt4 should uninstall the Adj-SIDs pointing to rt5
+# -rt5 should uninstall the Adj-SIDs pointing to rt4
+# -rt2 should reinstall rt5's Prefix-SIDs (2 nexthops deleted)
+# -rt3 should reinstall rt4's Prefix-SIDs (2 nexthops deleted)
+# -rt4 should reinstall rt3's Prefix-SIDs (1 nexthop deleted)
+# -rt4 should reinstall rt5's Prefix-SIDs (1 nexthop changed)
+# -rt5 should reinstall rt2's Prefix-SIDs (1 nexthop deleted)
+# -rt5 should reinstall rt4's Prefix-SIDs (1 nexthop changed)
+#
+def test_isis_adjacencies_step2():
+ logger.info("Test (step 2): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling IS-IS on the eth-rt5 interface on rt4")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "no ip router isis 1"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "no ipv6 router isis 1"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step2/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step2/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step2/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step2():
+ logger.info("Test (step 2): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step2/show_mpls_table.ref"
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Shut down the eth-rt4 interface on rt6
+# -Shut down the eth-rt5 interface on rt6
+#
+# Expected changes:
+# -All routers should uninstall rt6's Prefix-SIDs
+# -rt4 and rt5 should uninstall the Adj-SIDs pointing to rt6
+# -rt4 should reconverge rt5's Prefix-SIDs through rt2 using ECMP
+# -rt5 should reconverge rt4's Prefix-SIDs through rt3 using ECMP
+# -rt6 should uninstall all its IS-IS routes, Prefix-SIDs and Adj-SIDs
+#
+def test_isis_adjacencies_step3():
+ logger.info("Test (step 3): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Shutting down the eth-rt4 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "shutdown"')
+ logger.info("Shutting down the eth-rt5 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "shutdown"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step3/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step3/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step3/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step3():
+ logger.info("Test (step 3): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step3/show_mpls_table.ref"
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Bring up the eth-rt4 interface on rt6
+# -Bring up the eth-rt5 interface on rt6
+# -Change rt6's SRGB
+#
+# Expected changes:
+# -All routers should install rt6's Prefix-SIDs
+# -rt4 and rt5 should install Adj-SIDs for rt6
+# -rt4 should reconverge rt5's Prefix-SIDs through rt6 using the new SRGB
+# -rt5 should reconverge rt4's Prefix-SIDs through rt6 using the new SRGB
+# -rt6 should reinstall all IS-IS routes and Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5
+#
+def test_isis_adjacencies_step4():
+ logger.info("Test (step 4): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Bringing up the eth-rt4 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "no shutdown"')
+ logger.info("Bringing up the eth-rt5 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no shutdown"')
+ logger.info("Changing rt6's SRGB")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 18000 25999"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step4/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step4/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step4/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step4():
+ logger.info("Test (step 4): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step4/show_mpls_table.ref"
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Disable SR on rt6
+#
+# Expected changes:
+# -All routers should uninstall rt6's Prefix-SIDs
+# -rt4 should uninstall rt5's Prefix-SIDs since the nexthop router hasn't SR enabled anymore
+# -rt5 should uninstall rt4's Prefix-SIDs since the nexthop router hasn't SR enabled anymore
+# -rt6 should uninstall all Prefix-SIDs from the network, and the Adj-SIDs for rt4 and rt5
+#
+def test_isis_adjacencies_step5():
+ logger.info("Test (step 5): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling SR on rt6")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing on"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step5/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step5/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step5/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step5():
+ logger.info("Test (step 5): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step5/show_mpls_table.ref"
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Enable SR on rt6
+#
+# Expected changes:
+# -All routers should install rt6's Prefix-SIDs
+# -rt4 should install rt5's Prefix-SIDs through rt6
+# -rt5 should install rt4's Prefix-SIDs through rt6
+# -rt6 should install all Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5
+#
+def test_isis_adjacencies_step6():
+ logger.info("Test (step 6): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling SR on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing on"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step6/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step6/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step6/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step6():
+ logger.info("Test (step 6): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step6/show_mpls_table.ref"
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Delete rt1's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should uninstall rt1's Prefix-SIDs
+#
+def test_isis_adjacencies_step7():
+ logger.info("Test (step 7): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Deleting rt1's Prefix-SIDs")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 1.1.1.1/32 index 10"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 2001:db8:1000::1/128 index 11"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step7/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step7/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step7/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step7():
+ logger.info("Test (step 7): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step7/show_mpls_table.ref"
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Re-add rt1's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should install rt1's Prefix-SIDs
+#
+def test_isis_adjacencies_step8():
+ logger.info("Test (step 8): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-adding rt1's Prefix-SIDs")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step8/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step8/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step8/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step8():
+ logger.info("Test (step 8): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step8/show_mpls_table.ref"
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Change rt1's Prefix-SIDs to use the no-php option
+# -Change rt6's Prefix-SIDs to stop using the explicit-null option
+#
+# Expected changes:
+# -rt2 and rt3 should reinstall rt1's Prefix-SIDs accordingly
+# -rt4 and rt5 should reinstall rt6's Prefix-SIDs accordingly
+#
+def test_isis_adjacencies_step9():
+ logger.info("Test (step 9): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Changing rt1's Prefix-SIDs to use the no-php option")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10 no-php-flag"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11 no-php-flag"'
+ )
+ logger.info("Change rt6's Prefix-SIDs to stop using the explicit-null option")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 6.6.6.6/32 index 60"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::6/128 index 61"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step9/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step9/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step9/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step9():
+ logger.info("Test (step 9): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step9/show_mpls_table.ref"
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -Remove the IPv4 address from rt4's eth-rt2-1 interface
+#
+# Expected changes:
+# -rt2 should uninstall the IPv4 Adj-SIDs attached to the eth-rt4-1 interface
+# -rt2 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt4 (ECMP shouldn't be used anymore)
+# -rt4 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt2 (ECMP shouldn't be used anymore)
+#
+def test_isis_adjacencies_step10():
+ logger.info("Test (step 10): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing the IPv4 address from rt4's eth-rt2-1 interface")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2-1" -c "no ip address 10.0.2.4/24"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step10/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step10():
+ logger.info("Test (step 10): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step10/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step10():
+ logger.info("Test (step 10): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step10/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step10():
+ logger.info("Test (step 10): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step10/show_mpls_table.ref"
+ )
+
+
+#
+# Step 11
+#
+# Action(s):
+# -Enter invalid SR configuration
+#
+# Expected changes:
+# -All commands should be rejected
+#
+def test_isis_invalid_config_step11():
+ logger.info("Test (step 11): check if invalid configuration is rejected")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Entering invalid Segment Routing configuration...")
+ ret = tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10000"'
+ )
+ assert (
+ re.search("Configuration failed", ret) is not None
+ ), "Invalid SR configuration wasn't rejected"
+ ret = tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 14999"'
+ )
+ assert (
+ re.search("Configuration failed", ret) is not None
+ ), "Invalid SR configuration wasn't rejected"
+ ret = tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 16001"'
+ )
+ assert (
+ re.search("Configuration failed", ret) is not None
+ ), "Invalid SR configuration wasn't rejected"
+
+
+#
+# Step 12
+#
+# Action(s):
+# -Restore the original network setup
+#
+# Expected changes:
+# -All routes, Prefix-SIDs and Adj-SIDs should be the same as they were after the initial network convergence (step 1)
+#
+def test_isis_adjacencies_step12():
+ logger.info("Test (step 12): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Restoring the original network setup")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "ip router isis 1"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "ipv6 router isis 1"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "isis network point-to-point"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "isis hello-multiplier 3"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 16000 23999"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 1.1.1.1/32 index 10"'
+ )
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::1/128 index 11"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 6.6.6.6/32 index 60 explicit-null"'
+ )
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::6/128 index 61 explicit-null"'
+ )
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2-1" -c "ip address 10.0.2.4/24"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "step1/show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib_ipv4_step12():
+ logger.info("Test (step 12): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", "step1/show_ip_route.ref"
+ )
+
+
+def test_rib_ipv6_step12():
+ logger.info("Test (step 12): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", "step1/show_ipv6_route.ref"
+ )
+
+
+def test_mpls_lib_step12():
+ logger.info("Test (step 12): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table.ref"
+ )
+
+
+# Memory leak test template
+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/isis_te_topo1/__init__.py b/tests/topotests/isis_te_topo1/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/__init__.py
diff --git a/tests/topotests/isis_te_topo1/r1/isisd.conf b/tests/topotests/isis_te_topo1/r1/isisd.conf
new file mode 100644
index 0000000..938d713
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r1/isisd.conf
@@ -0,0 +1,31 @@
+!
+hostname r1
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r1-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r1-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis TE
+ net 49.0000.0000.0000.0001.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.1
+!
+
diff --git a/tests/topotests/isis_te_topo1/r1/zebra.conf b/tests/topotests/isis_te_topo1/r1/zebra.conf
new file mode 100644
index 0000000..814d8f8
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r1/zebra.conf
@@ -0,0 +1,25 @@
+!
+hostname r1
+!
+interface lo
+ ip address 10.0.255.1/32
+ ipv6 address 2001:db8:ffff::1/128
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ link-params
+ metric 20
+ delay 10000
+ ava-bw 1.25e+08
+ enable
+ exit-link-params
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8:1::1:1/64
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/isis_te_topo1/r2/isisd.conf b/tests/topotests/isis_te_topo1/r2/isisd.conf
new file mode 100644
index 0000000..f389e90
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r2/isisd.conf
@@ -0,0 +1,45 @@
+!
+hostname r2
+!
+! debug isis te-events
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r2-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth1
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth2
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r2-eth3
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+router isis TE
+ net 49.0000.0000.0000.0002.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.2
+!
diff --git a/tests/topotests/isis_te_topo1/r2/zebra.conf b/tests/topotests/isis_te_topo1/r2/zebra.conf
new file mode 100644
index 0000000..26b48de
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r2/zebra.conf
@@ -0,0 +1,39 @@
+!
+hostname r2
+!
+interface lo
+ ip address 10.0.255.2/32
+ ipv6 address 2001:db8:ffff::2/128
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth1
+ ip address 10.0.1.2/24
+ ipv6 address 2001:db8:1::1:2/64
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth2
+ ip address 10.0.3.2/24
+ ipv6 address 2001:db8:3::3:2/64
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth3
+ ip address 10.0.4.2/24
+ ipv6 address 2001:db8:4::4:2/64
+ link-params
+ metric 30
+ delay 25000
+ use-bw 1.25e+8
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/isis_te_topo1/r3/isisd.conf b/tests/topotests/isis_te_topo1/r3/isisd.conf
new file mode 100644
index 0000000..4920a17
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r3/isisd.conf
@@ -0,0 +1,34 @@
+!
+hostname r3
+!
+! debug isis te-events
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r3-eth0
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r3-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+!
+router isis TE
+ net 49.0000.0000.0000.0003.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.3
+ mpls-te router-address ipv6 2001:db8:1000::3
+!
diff --git a/tests/topotests/isis_te_topo1/r3/zebra.conf b/tests/topotests/isis_te_topo1/r3/zebra.conf
new file mode 100644
index 0000000..6a82f30
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r3/zebra.conf
@@ -0,0 +1,25 @@
+!
+hostname r3
+!
+interface lo
+ ip address 10.0.255.3/32
+ ipv6 address 2001:db8:ffff::3/128
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+ ipv6 address 2001:db8:3::3:3/64
+ link-params
+ enable
+ admin-grp 0x20
+ exit-link-params
+!
+interface r3-eth1
+ ipv6 address 2001:db8:5::4:3/64
+ link-params
+ enable
+ metric 10
+ delay 50000
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/isis_te_topo1/r4/isisd.conf b/tests/topotests/isis_te_topo1/r4/isisd.conf
new file mode 100644
index 0000000..51e9ba6
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r4/isisd.conf
@@ -0,0 +1,39 @@
+!
+hostname r4
+!
+! debug isis te-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis TE
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis passive
+!
+interface r4-eth0
+ ip router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+interface r4-eth1
+ ipv6 router isis TE
+ isis circuit-type level-2-only
+ isis network point-to-point
+ isis hello-multiplier 3
+!
+!
+router isis TE
+ net 49.0000.0000.0000.0004.00
+ is-type level-2-only
+ topology ipv6-unicast
+ lsp-timers gen-interval 2 refresh-interval 10 max-lifetime 350
+ mpls-te on
+ mpls-te router-address 10.0.255.4
+ segment-routing on
+ segment-routing global-block 10000 19999 local-block 5000 5999
+ segment-routing node-msd 12
+ segment-routing prefix 10.0.255.4/32 index 400 no-php-flag
+ segment-routing prefix 2001:db8:ffff::4/128 index 1400 no-php-flag
+!
diff --git a/tests/topotests/isis_te_topo1/r4/zebra.conf b/tests/topotests/isis_te_topo1/r4/zebra.conf
new file mode 100644
index 0000000..2f6a38e
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/r4/zebra.conf
@@ -0,0 +1,22 @@
+!
+hostname r4
+!
+interface lo
+ ip address 10.0.255.4/32
+ ipv6 address 2001:db8:ffff::4/128
+!
+interface r4-eth0
+ ip address 10.0.4.4/24
+ ipv6 address 2001:db8:4::2:4/64
+ link-params
+ enable
+ exit-link-params
+!
+interface r4-eth1
+ ipv6 address 2001:db8:5::3:4/64
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step1.json b/tests/topotests/isis_te_topo1/reference/ted_step1.json
new file mode 100644
index 0000000..027dd80
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step1.json
@@ -0,0 +1,840 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":14,
+ "subnetsCount":22,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":65537,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:1",
+ "remote-address-v6":"2001:db8:1::1:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":65538,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8:1::1:1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step2.json b/tests/topotests/isis_te_topo1/reference/ted_step2.json
new file mode 100644
index 0000000..2e9a5ec
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step2.json
@@ -0,0 +1,644 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":10,
+ "subnetsCount":18,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step3.json b/tests/topotests/isis_te_topo1/reference/ted_step3.json
new file mode 100644
index 0000000..5c7ccdd
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step3.json
@@ -0,0 +1,744 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":12,
+ "subnetsCount":20,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8::1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step4.json b/tests/topotests/isis_te_topo1/reference/ted_step4.json
new file mode 100644
index 0000000..5c7ccdd
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step4.json
@@ -0,0 +1,744 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":12,
+ "subnetsCount":20,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8::1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step5.json b/tests/topotests/isis_te_topo1/reference/ted_step5.json
new file mode 100644
index 0000000..48d475c
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step5.json
@@ -0,0 +1,940 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":16,
+ "subnetsCount":24,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":65537,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:1",
+ "remote-address-v6":"2001:db8:1::1:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":65538,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8::1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/reference/ted_step6.json b/tests/topotests/isis_te_topo1/reference/ted_step6.json
new file mode 100644
index 0000000..75443a4
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/reference/ted_step6.json
@@ -0,0 +1,941 @@
+{
+ "ted":{
+ "name":"ISIS",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":16,
+ "subnetsCount":24,
+ "vertices":[
+ {
+ "vertex-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r1",
+ "router-id":"10.0.255.1"
+ },
+ {
+ "vertex-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r2",
+ "router-id":"10.0.255.2"
+ },
+ {
+ "vertex-id":3,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r3",
+ "router-id":"10.0.255.3",
+ "router-id-v6":"2001:db8:1000::3"
+ },
+ {
+ "vertex-id":4,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "name":"r4",
+ "router-id":"10.0.255.4",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":1,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address-v6":"2001:db8::1",
+ "remote-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":2,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8::2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":65537,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:1",
+ "remote-address-v6":"2001:db8:1::1:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":65538,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:1::1:2",
+ "remote-address-v6":"2001:db8:1::1:1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196610,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:3::3:2",
+ "remote-address-v6":"2001:db8:3::3:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196611,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address-v6":"2001:db8:3::3:3",
+ "remote-address-v6":"2001:db8:3::3:2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":196612,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address-v6":"2001:db8:5::3:4",
+ "remote-address-v6":"2001:db8:5::4:3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0xb0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":262147,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address-v6":"2001:db8:5::4:3",
+ "remote-address-v6":"2001:db8:5::3:4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":50000
+ }
+ },
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "local-vertex-id":1,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":1,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":3,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.3",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772931,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "local-vertex-id":3,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.3",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "local-vertex-id":2,
+ "remote-vertex-id":4,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.4",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000
+ }
+ },
+ {
+ "edge-id":167773188,
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "local-vertex-id":4,
+ "remote-vertex-id":2,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.4",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5000,
+ "flags":"0x30",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.3\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.4\/24",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ },
+ {
+ "subnet-id":"2001:db8::1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8::2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:1\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:1::1:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:2\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:3::3:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::3:4\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:5::4:3\/64",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::1\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0001",
+ "vertex-id":1,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::2\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0002",
+ "vertex-id":2,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::3\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0003",
+ "vertex-id":3,
+ "metric":10
+ },
+ {
+ "subnet-id":"2001:db8:ffff::4\/128",
+ "status":"Sync",
+ "origin":"ISIS_L2",
+ "advertised-router":"0000.0000.0004",
+ "vertex-id":4,
+ "metric":10,
+ "segment-routing":{
+ "pref-sid":1400,
+ "algo":0,
+ "flags":"0x60"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_te_topo1/test_isis_te_topo1.py b/tests/topotests/isis_te_topo1/test_isis_te_topo1.py
new file mode 100644
index 0000000..0e453d3
--- /dev/null
+++ b/tests/topotests/isis_te_topo1/test_isis_te_topo1.py
@@ -0,0 +1,265 @@
+#!/usr/bin/env python
+
+#
+# test_isis_te_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+# 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_isis_te_topo1.py: Test the FRR IS-IS with Traffic Engineering.
+
+ +------------+
+ | |
+ | R1 |
+ | 10.0.225.1 |
+ | |
+ +------------+
+ r1-eth0| |r1-eth1
+ | |
+ 10.0.0.0/24| |10.0.1.0/24
+ | |2001:db8:1:/64
+ | |
+ r2-eth0| |r2-eth1
+ +------------+ +------------+
+ | | | |
+ | R2 |r2-eth2 r3-eth0| R3 |
+ | 10.0.255.2 +------------------+ 10.0.255.3 |
+ | | 10.0.3.0/24 | |
+ +------------+ 2001:db8:3:/64 +------+-----+
+ r2-eth3| r3-eth1|
+ | |
+ 10.0.4.0/24| |
+ | |
+ | |
+ r4-eth0| 2001:db8:5:/64|
+ +------------+ |
+ | | |
+ | R4 |r4-eth1 |
+ | 10.0.255.4 +-------------------------+
+ | |
+ +------------+
+
+"""
+
+import os
+import sys
+import json
+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
+
+# and Finally pytest
+import pytest
+
+pytestmark = [pytest.mark.isisd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Interconect router 1 and 2 with 2 links
+ 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["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 3 and 2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 4 and 2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconnect router 3 and 4
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ logger.info("\n\n---- Starting IS-IS TE tests ----\n")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module():
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+ logger.info("\n\n---- IS-IS TE tests End ----\n")
+
+
+def compare_ted_json_output(tgen, rname, fileref):
+ "Compare TED JSON output"
+
+ logger.info('Comparing router "%s" TED output', rname)
+
+ filename = "{}/reference/{}".format(CWD, fileref)
+ expected = json.loads(open(filename).read())
+ command = "show isis mpls-te database json"
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def setup_testcase(msg):
+ "Setup test case"
+
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+# Note that all routers must discover the same Network Topology, so the same TED.
+
+
+def test_step1():
+ "Step1: Check initial topology"
+
+ tgen = setup_testcase("Step1: test initial IS-IS TE Data Base")
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step1.json")
+
+
+def test_step2():
+ "Step2: Shutdown interface between r1 and r2 and verify that \
+ corresponding Edges are removed from the TED on all routers "
+
+ tgen = setup_testcase("Step2: Shutdown interface between r1 & r2")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step2.json")
+
+
+def test_step3():
+ "Step3: Enable IPv6 address between r1 and r2 and verify that \
+ corresponding Edges are added in the TED on all routers"
+
+ tgen = setup_testcase("Step3: Add IPv6 on r1 and r2 interfaces")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth0" -c "ipv6 address 2001:db8:0::1/64"')
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth0" -c "ipv6 router isis TE"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth0" -c "ipv6 address 2001:db8:0::2/64"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth0" -c "ipv6 router isis TE"')
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step3.json")
+
+
+def test_step4():
+ "Step4: Modify Segment Routing Prefix SID advertisement on Router r4"
+
+ tgen = setup_testcase("Step4: Modify Prefix SID on router r4")
+
+ tgen.net["r4"].cmd('vtysh -c "conf t" -c "router isis TE" -c "segment-routing prefix 10.0.255.4/32 index 40"')
+ tgen.net["r4"].cmd('vtysh -c "conf t" -c "router isis TE" -c "segment-routing prefix 2001:db8:ffff::4/128 index 1040"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step4.json")
+
+
+def test_step5():
+ "Step5: Re-enable interface between r1 & r2 and verify that corresponding \
+ Edges are added in the TED on all routers"
+
+ tgen = setup_testcase("Step5: Re-enable interface between r1 & r2")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step5.json")
+
+
+def test_step6():
+ "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \
+ for interface r2-eth3 on r2 and verify that corresponding Edges are \
+ updated in the TED on all routers"
+
+ tgen = setup_testcase("Step6: Modify link parameters on r2 & r4")
+
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"')
+ tgen.net["r4"].cmd('vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"')
+ tgen.net["r4"].cmd('vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step6.json")
+
+
+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/isis_tilfa_topo1/__init__.py b/tests/topotests/isis_tilfa_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/__init__.py
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf
new file mode 100644
index 0000000..955bd5c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/isisd.conf
@@ -0,0 +1,33 @@
+password 1
+hostname rt1
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis priority 100
+ isis fast-reroute ti-lfa
+!
+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
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+ segment-routing prefix 2001:db8:1000::1/128 index 11
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..92b7437
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,294 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..3232121
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_ipv6_route.ref
@@ -0,0 +1,121 @@
+{
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_mpls_table.ref
new file mode 100644
index 0000000..aa0357d
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_mpls_table.ref
@@ -0,0 +1,134 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..26f0dff
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,32 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step2/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step3/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..10b336f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ip_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step3/show_ip_route.ref
++++ b/rt1/step4/show_ip_route.ref
+@@ -60,10 +60,7 @@
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..904aaa1
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step3/show_ipv6_route.ref
++++ b/rt1/step4/show_ipv6_route.ref
+@@ -57,10 +57,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..d7d8753
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step4/show_mpls_table.ref.diff
@@ -0,0 +1,33 @@
+--- a/rt1/step3/show_mpls_table.ref
++++ b/rt1/step4/show_mpls_table.ref
+@@ -47,30 +47,6 @@
+ }
+ ]
+ },
+- "16040":{
+- "inLabel":16040,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "installed":true,
+- "nexthop":"10.0.1.2"
+- }
+- ]
+- },
+- "16041":{
+- "inLabel":16041,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "installed":true,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..b583fa9
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ip_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step4/show_ip_route.ref
++++ b/rt1/step5/show_ip_route.ref
+@@ -60,7 +60,10 @@
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..d608abe
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step4/show_ipv6_route.ref
++++ b/rt1/step5/show_ipv6_route.ref
+@@ -57,7 +57,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..b5161fc
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step5/show_mpls_table.ref.diff
@@ -0,0 +1,33 @@
+--- a/rt1/step4/show_mpls_table.ref
++++ b/rt1/step5/show_mpls_table.ref
+@@ -47,6 +47,30 @@
+ }
+ ]
+ },
++ "16040":{
++ "inLabel":16040,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "installed":true,
++ "nexthop":"10.0.1.2"
++ }
++ ]
++ },
++ "16041":{
++ "inLabel":16041,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "installed":true,
++ "interface":"eth-sw1"
++ }
++ ]
++ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step6/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..726aed5
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ip_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step6/show_ip_route.ref
++++ b/rt1/step7/show_ip_route.ref
+@@ -83,10 +83,7 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..2049f6f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step6/show_ipv6_route.ref
++++ b/rt1/step7/show_ipv6_route.ref
+@@ -79,10 +79,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..22301ba
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step7/show_mpls_table.ref.diff
@@ -0,0 +1,33 @@
+--- a/rt1/step6/show_mpls_table.ref
++++ b/rt1/step7/show_mpls_table.ref
+@@ -71,30 +71,6 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.1.3"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..4a1d480
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ip_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step7/show_ip_route.ref
++++ b/rt1/step8/show_ip_route.ref
+@@ -83,7 +83,10 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..eaece74
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,14 @@
+--- a/rt1/step7/show_ipv6_route.ref
++++ b/rt1/step8/show_ipv6_route.ref
+@@ -79,7 +79,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..46c17de
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step8/show_mpls_table.ref.diff
@@ -0,0 +1,33 @@
+--- a/rt1/step7/show_mpls_table.ref
++++ b/rt1/step8/show_mpls_table.ref
+@@ -71,6 +71,30 @@
+ }
+ ]
+ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
++ "nexthop":"10.0.1.3"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "installed":true,
++ "interface":"eth-sw1"
++ }
++ ]
++ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..06efdc9
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ip_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt1/step8/show_ip_route.ref
++++ b/rt1/step9/show_ip_route.ref
+@@ -85,7 +85,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..a58f2d4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt1/step8/show_ipv6_route.ref
++++ b/rt1/step9/show_ipv6_route.ref
+@@ -81,7 +81,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..c0a1ac5
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/step9/show_mpls_table.ref.diff
@@ -0,0 +1,64 @@
+--- a/rt1/step8/show_mpls_table.ref
++++ b/rt1/step9/show_mpls_table.ref
+@@ -71,30 +71,6 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.1.3"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+@@ -129,6 +105,30 @@
+ "installed":true,
+ "interface":"eth-sw1"
+ }
++ ]
++ },
++ "16500":{
++ "inLabel":16500,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16500,
++ "installed":true,
++ "nexthop":"10.0.1.3"
++ }
++ ]
++ },
++ "16501":{
++ "inLabel":16501,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16501,
++ "installed":true,
++ "interface":"eth-sw1"
++ }
+ ]
+ }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt1/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..37b3f27
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt1/zebra.conf
@@ -0,0 +1,19 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf
new file mode 100644
index 0000000..f971c65
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/isisd.conf
@@ -0,0 +1,45 @@
+hostname rt2
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+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
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20
+ segment-routing prefix 2001:db8:1000::2/128 index 21
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000..7e1ccd1
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ip_route.ref
@@ -0,0 +1,563 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050,
+ 16010
+ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050,
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050,
+ 16030
+ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050,
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050,
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16060
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..6d31f6f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_ipv6_route.ref
@@ -0,0 +1,229 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16051,
+ 16011
+ ]
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16051,
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16051,
+ 16031
+ ]
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16051,
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16051,
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16061
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_mpls_table.ref
new file mode 100644
index 0000000..b9b906a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_mpls_table.ref
@@ -0,0 +1,286 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.3",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.3.4",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.2.4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt4-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt4-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt4-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt4-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..1ea72a5
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,70 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..90e0895
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ip_route.ref.diff
@@ -0,0 +1,169 @@
+--- a/rt2/step1/show_ip_route.ref
++++ b/rt2/step2/show_ip_route.ref
+@@ -15,36 +15,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- }
+ ]
+ }
+ ],
+@@ -64,36 +38,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- }
+ ]
+ }
+ ],
+@@ -251,40 +199,12 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
++ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+@@ -380,24 +300,6 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+@@ -418,24 +320,6 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..2d19f20
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_ipv6_route.ref.diff
@@ -0,0 +1,72 @@
+--- a/rt2/step1/show_ipv6_route.ref
++++ b/rt2/step2/show_ipv6_route.ref
+@@ -14,34 +14,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- }
+ ]
+ }
+ ],
+@@ -60,34 +36,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- }
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..01fc74a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step2/show_mpls_table.ref.diff
@@ -0,0 +1,102 @@
+--- a/rt2/step1/show_mpls_table.ref
++++ b/rt2/step2/show_mpls_table.ref
+@@ -7,23 +7,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+@@ -35,23 +19,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
+@@ -63,23 +31,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+@@ -91,23 +43,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..d93f036
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ip_route.ref.diff
@@ -0,0 +1,169 @@
+--- a/rt2/step2/show_ip_route.ref
++++ b/rt2/step3/show_ip_route.ref
+@@ -15,10 +15,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ }
+ ]
+ }
+ ],
+@@ -38,10 +64,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ }
+ ]
+ }
+ ],
+@@ -199,12 +251,40 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -300,6 +380,24 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+@@ -320,6 +418,24 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..68b618e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_ipv6_route.ref.diff
@@ -0,0 +1,72 @@
+--- a/rt2/step2/show_ipv6_route.ref
++++ b/rt2/step3/show_ipv6_route.ref
+@@ -14,10 +14,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ }
+ ]
+ }
+ ],
+@@ -36,10 +60,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ }
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..966e153
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step3/show_mpls_table.ref.diff
@@ -0,0 +1,102 @@
+--- a/rt2/step2/show_mpls_table.ref
++++ b/rt2/step3/show_mpls_table.ref
+@@ -7,7 +7,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1"
++ "nexthop":"10.0.1.1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -19,7 +35,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+@@ -31,7 +63,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3"
++ "nexthop":"10.0.1.3",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -43,7 +91,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..dd75d76
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ip_route.ref.diff
@@ -0,0 +1,192 @@
+--- a/rt2/step3/show_ip_route.ref
++++ b/rt2/step4/show_ip_route.ref
+@@ -15,36 +15,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- }
+ ]
+ }
+ ],
+@@ -64,36 +38,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- }
+ ]
+ }
+ ],
+@@ -115,9 +63,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ },
+ {
+@@ -128,9 +73,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -141,8 +83,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050,
+- 16040
++ 16050
+ ]
+ }
+ ]
+@@ -173,20 +114,14 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -209,9 +144,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16060
+ ]
+ },
+ {
+@@ -222,9 +154,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16060
+ ]
+ }
+ ],
+@@ -251,40 +180,12 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
++ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..6373123
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,146 @@
+--- a/rt2/step3/show_ipv6_route.ref
++++ b/rt2/step4/show_ipv6_route.ref
+@@ -14,34 +14,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- }
+ ]
+ }
+ ],
+@@ -60,34 +36,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- }
+ ]
+ }
+ ],
+@@ -108,9 +60,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ },
+ {
+@@ -120,9 +69,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -132,8 +78,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16051,
+- 16041
++ 16051
+ ]
+ }
+ ]
+@@ -153,10 +98,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+@@ -171,10 +113,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -196,9 +135,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16061
+ ]
+ },
+ {
+@@ -208,9 +144,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16061
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..3872ce4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step4/show_mpls_table.ref.diff
@@ -0,0 +1,200 @@
+--- a/rt2/step3/show_mpls_table.ref
++++ b/rt2/step4/show_mpls_table.ref
+@@ -7,23 +7,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+@@ -35,23 +19,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
+@@ -63,23 +31,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+@@ -91,84 +43,6 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
+- }
+- ]
+- },
+- "16040":{
+- "inLabel":16040,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.3.4",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.2.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.1.3"
+- }
+- ]
+- },
+- "16041":{
+- "inLabel":16041,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt4-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt4-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+ "interface":"eth-sw1"
+ }
+ ]
+@@ -181,18 +55,6 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
+- "nexthop":"10.0.3.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+@@ -204,18 +66,6 @@
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+- "installed":true,
+- "interface":"eth-rt4-2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..4d56364
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ip_route.ref.diff
@@ -0,0 +1,192 @@
+--- a/rt2/step4/show_ip_route.ref
++++ b/rt2/step5/show_ip_route.ref
+@@ -15,10 +15,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ }
+ ]
+ }
+ ],
+@@ -38,10 +64,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ }
+ ]
+ }
+ ],
+@@ -63,6 +115,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ },
+ {
+@@ -73,6 +128,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -83,7 +141,8 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16050,
++ 16040
+ ]
+ }
+ ]
+@@ -114,14 +173,20 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -144,6 +209,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16060
+ ]
+ },
+ {
+@@ -154,6 +222,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16060
+ ]
+ }
+ ],
+@@ -180,12 +251,40 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..f9e0276
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,146 @@
+--- a/rt2/step4/show_ipv6_route.ref
++++ b/rt2/step5/show_ipv6_route.ref
+@@ -14,10 +14,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ }
+ ]
+ }
+ ],
+@@ -36,10 +60,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ }
+ ]
+ }
+ ],
+@@ -60,6 +108,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ },
+ {
+@@ -69,6 +120,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -78,7 +132,8 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16051
++ 16051,
++ 16041
+ ]
+ }
+ ]
+@@ -98,7 +153,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ },
+ {
+ "fib":true,
+@@ -113,7 +171,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
+@@ -135,6 +196,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16061
+ ]
+ },
+ {
+@@ -144,6 +208,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16061
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..6aebbd6
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step5/show_mpls_table.ref.diff
@@ -0,0 +1,200 @@
+--- a/rt2/step4/show_mpls_table.ref
++++ b/rt2/step5/show_mpls_table.ref
+@@ -7,7 +7,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1"
++ "nexthop":"10.0.1.1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -19,7 +35,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+@@ -31,7 +63,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3"
++ "nexthop":"10.0.1.3",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -43,6 +91,84 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
++ }
++ ]
++ },
++ "16040":{
++ "inLabel":16040,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.3.4",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.2.4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.1.3"
++ }
++ ]
++ },
++ "16041":{
++ "inLabel":16041,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt4-2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt4-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
+ "interface":"eth-sw1"
+ }
+ ]
+@@ -55,6 +181,18 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "installed":true,
++ "nexthop":"10.0.3.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+@@ -66,6 +204,18 @@
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
++ "installed":true,
++ "interface":"eth-rt4-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "installed":true,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step6/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..5e73b97
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ip_route.ref.diff
@@ -0,0 +1,288 @@
+--- a/rt2/step6/show_ip_route.ref
++++ b/rt2/step7/show_ip_route.ref
+@@ -15,36 +15,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16010
+- ]
+- }
+ ]
+ }
+ ],
+@@ -64,36 +38,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050,
+- 16030
+- ]
+- }
+ ]
+ }
+ ],
+@@ -113,9 +61,6 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -126,25 +71,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050,
+- 16040
+- ]
+- }
+ ]
+ }
+ ],
+@@ -163,30 +93,21 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -251,40 +172,12 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
++ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.2.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
+- },
+- {
+- "ip":"10.0.3.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+@@ -299,30 +192,13 @@
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt4-1",
+- "backupIndex":[
+- 0
+- ]
++ "interfaceName":"eth-rt4-1"
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -338,29 +214,12 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt4-2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "interfaceName":"eth-rt4-2"
+ }
+ ]
+ }
+@@ -497,31 +356,14 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.3",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..5dc4e59
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,139 @@
+--- a/rt2/step6/show_ipv6_route.ref
++++ b/rt2/step7/show_ipv6_route.ref
+@@ -14,34 +14,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16011
+- ]
+- }
+ ]
+ }
+ ],
+@@ -60,34 +36,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051,
+- 16031
+- ]
+- }
+ ]
+ }
+ ],
+@@ -106,9 +58,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -118,24 +67,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16051,
+- 16041
+- ]
+- }
+ ]
+ }
+ ],
+@@ -153,28 +88,19 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..6c0d739
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step7/show_mpls_table.ref.diff
@@ -0,0 +1,207 @@
+--- a/rt2/step6/show_mpls_table.ref
++++ b/rt2/step7/show_mpls_table.ref
+@@ -7,23 +7,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+@@ -35,23 +19,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
+@@ -63,23 +31,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+@@ -91,23 +43,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
+@@ -119,26 +55,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.3.4",
+- "backupIndex":[
+- 0
+- ]
++ "nexthop":"10.0.3.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.2.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.1.3"
++ "nexthop":"10.0.2.4"
+ }
+ ]
+ },
+@@ -150,74 +73,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt4-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt4-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.3.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.1.3"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+ "interface":"eth-rt4-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-sw1"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..f5df607
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ip_route.ref.diff
@@ -0,0 +1,288 @@
+--- a/rt2/step7/show_ip_route.ref
++++ b/rt2/step8/show_ip_route.ref
+@@ -15,10 +15,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16010
++ ]
++ }
+ ]
+ }
+ ],
+@@ -38,10 +64,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050,
++ 16030
++ ]
++ }
+ ]
+ }
+ ],
+@@ -61,6 +113,9 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -71,10 +126,25 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16050,
++ 16040
++ ]
++ }
+ ]
+ }
+ ],
+@@ -93,21 +163,30 @@
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -172,12 +251,40 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
+ },
+ {
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.2.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
++ },
++ {
++ "ip":"10.0.3.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -192,13 +299,30 @@
+ {
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt4-1"
++ "interfaceName":"eth-rt4-1",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -214,12 +338,29 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt4-2"
++ "interfaceName":"eth-rt4-2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -356,14 +497,31 @@
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.3",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..125f36b
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,139 @@
+--- a/rt2/step7/show_ipv6_route.ref
++++ b/rt2/step8/show_ipv6_route.ref
+@@ -14,10 +14,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16011
++ ]
++ }
+ ]
+ }
+ ],
+@@ -36,10 +60,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4-2",
++ "active":true,
++ "labels":[
++ 16051,
++ 16031
++ ]
++ }
+ ]
+ }
+ ],
+@@ -58,6 +106,9 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -67,10 +118,24 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16051,
++ 16041
++ ]
++ }
+ ]
+ }
+ ],
+@@ -88,19 +153,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..a1d5d79
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step8/show_mpls_table.ref.diff
@@ -0,0 +1,207 @@
+--- a/rt2/step7/show_mpls_table.ref
++++ b/rt2/step8/show_mpls_table.ref
+@@ -7,7 +7,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1"
++ "nexthop":"10.0.1.1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -19,7 +35,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+@@ -31,7 +63,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.3"
++ "nexthop":"10.0.1.3",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+@@ -43,7 +91,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4-2"
+ }
+ ]
+ },
+@@ -55,13 +119,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.3.4"
++ "nexthop":"10.0.3.4",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.2.4"
++ "nexthop":"10.0.2.4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+@@ -73,13 +150,74 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt4-2"
++ "interface":"eth-rt4-2",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
++ "interface":"eth-rt4-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-sw1"
++ }
++ ]
++ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
++ "nexthop":"10.0.3.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "installed":true,
++ "nexthop":"10.0.1.3"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "installed":true,
++ "interface":"eth-rt4-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "installed":true,
+ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "installed":true,
++ "interface":"eth-sw1"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..2475c63
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ip_route.ref.diff
@@ -0,0 +1,119 @@
+--- a/rt2/step8/show_ip_route.ref
++++ b/rt2/step9/show_ip_route.ref
+@@ -31,7 +31,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16050,
++ 16500,
+ 16010
+ ]
+ },
+@@ -41,7 +41,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16050,
++ 16500,
+ 16010
+ ]
+ }
+@@ -80,7 +80,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16050,
++ 16500,
+ 16030
+ ]
+ },
+@@ -90,7 +90,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16050,
++ 16500,
+ 16030
+ ]
+ }
+@@ -141,7 +141,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050,
++ 16500,
+ 16040
+ ]
+ }
+@@ -165,7 +165,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ },
+ {
+@@ -175,7 +175,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ },
+ {
+@@ -185,7 +185,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
+@@ -274,7 +274,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ },
+ {
+@@ -283,7 +283,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
+@@ -321,7 +321,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
+@@ -359,7 +359,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
+@@ -520,7 +520,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..2d21fbc
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,74 @@
+--- a/rt2/step8/show_ipv6_route.ref
++++ b/rt2/step9/show_ipv6_route.ref
+@@ -29,7 +29,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16051,
++ 16501,
+ 16011
+ ]
+ },
+@@ -38,7 +38,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16051,
++ 16501,
+ 16011
+ ]
+ }
+@@ -75,7 +75,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16051,
++ 16501,
+ 16031
+ ]
+ },
+@@ -84,7 +84,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16051,
++ 16501,
+ 16031
+ ]
+ }
+@@ -132,7 +132,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16051,
++ 16501,
+ 16041
+ ]
+ }
+@@ -155,7 +155,7 @@
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ },
+ {
+@@ -164,7 +164,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ },
+ {
+@@ -173,7 +173,7 @@
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..bc0ec31
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/step9/show_mpls_table.ref.diff
@@ -0,0 +1,182 @@
+--- a/rt2/step8/show_mpls_table.ref
++++ b/rt2/step9/show_mpls_table.ref
+@@ -17,12 +17,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+@@ -45,12 +45,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":16501,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":16501,
+ "interface":"eth-rt4-2"
+ }
+ ]
+@@ -73,12 +73,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+@@ -101,12 +101,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":16501,
+ "interface":"eth-rt4-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":16501,
+ "interface":"eth-rt4-2"
+ }
+ ]
+@@ -137,7 +137,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+@@ -168,55 +168,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.3.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.2.4"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "installed":true,
+- "nexthop":"10.0.1.3"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-rt4-2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
+- "interface":"eth-rt4-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "installed":true,
++ "outLabel":16501,
+ "interface":"eth-sw1"
+ }
+ ]
+@@ -282,5 +234,53 @@
+ "interface":"eth-sw1"
+ }
+ ]
++ },
++ "16500":{
++ "inLabel":16500,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16500,
++ "installed":true,
++ "nexthop":"10.0.3.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16500,
++ "installed":true,
++ "nexthop":"10.0.2.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16500,
++ "installed":true,
++ "nexthop":"10.0.1.3"
++ }
++ ]
++ },
++ "16501":{
++ "inLabel":16501,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16501,
++ "installed":true,
++ "interface":"eth-rt4-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16501,
++ "installed":true,
++ "interface":"eth-rt4-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16501,
++ "installed":true,
++ "interface":"eth-sw1"
++ }
++ ]
+ }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt2/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..f9ac098
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt2/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf
new file mode 100644
index 0000000..64f091c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/isisd.conf
@@ -0,0 +1,45 @@
+hostname rt3
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-sw1
+ ip router isis 1
+ ipv6 router isis 1
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+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 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30
+ segment-routing prefix 2001:db8:1000::3/128 index 31
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000..d70e9fe
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ip_route.ref
@@ -0,0 +1,563 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040,
+ 16010
+ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040,
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040,
+ 16020
+ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040,
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040,
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16060
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..058d336
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_ipv6_route.ref
@@ -0,0 +1,229 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16041,
+ 16011
+ ]
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16041,
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16041,
+ 16021
+ ]
+ },
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16041,
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16041,
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16061
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16061
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_mpls_table.ref
new file mode 100644
index 0000000..1912df3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_mpls_table.ref
@@ -0,0 +1,286 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.5.5"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt5-2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.2",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.5.5"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-sw1",
+ "backupIndex":[
+ 0,
+ 1
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt5-2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.5.5",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.4.5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "installed":true,
+ "interface":"eth-rt5-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "interface":"eth-sw1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..d174b4a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,70 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt5-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-sw1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "hold-timer": 9,
+ "neighbor-priority": 100,
+ "state": "up"
+ },
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 64,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step2/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step3/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..9ba73b0
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ip_route.ref.diff
@@ -0,0 +1,288 @@
+--- a/rt3/step3/show_ip_route.ref
++++ b/rt3/step4/show_ip_route.ref
+@@ -15,36 +15,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.4.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16040,
+- 16010
+- ]
+- },
+- {
+- "ip":"10.0.5.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16040,
+- 16010
+- ]
+- }
+ ]
+ }
+ ],
+@@ -64,36 +38,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.4.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16040,
+- 16020
+- ]
+- },
+- {
+- "ip":"10.0.5.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16040,
+- 16020
+- ]
+- }
+ ]
+ }
+ ],
+@@ -112,30 +60,21 @@
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -156,9 +95,6 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -169,25 +105,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040,
+- 16050
+- ]
+- }
+ ]
+ }
+ ],
+@@ -251,40 +172,12 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
++ "interfaceName":"eth-sw1"
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.4.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
+- },
+- {
+- "ip":"10.0.5.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "interfaceName":"eth-sw1"
+ }
+ ]
+ }
+@@ -375,30 +268,13 @@
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-1",
+- "backupIndex":[
+- 0
+- ]
++ "interfaceName":"eth-rt5-1"
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -414,29 +290,12 @@
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "interfaceName":"eth-rt5-2"
+ }
+ ]
+ }
+@@ -531,31 +390,14 @@
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.1.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..04f61c4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,139 @@
+--- a/rt3/step3/show_ipv6_route.ref
++++ b/rt3/step4/show_ipv6_route.ref
+@@ -14,34 +14,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16041,
+- 16011
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16041,
+- 16011
+- ]
+- }
+ ]
+ }
+ ],
+@@ -60,34 +36,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
+- "backupIndex":[
+- 0,
+- 1
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16041,
+- 16021
+- ]
+- },
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16041,
+- 16021
+- ]
+- }
+ ]
+ }
+ ],
+@@ -105,28 +57,19 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -146,9 +89,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -158,24 +98,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-sw1",
+- "active":true,
+- "labels":[
+- 16041,
+- 16051
+- ]
+- }
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..b3588ca
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step4/show_mpls_table.ref.diff
@@ -0,0 +1,206 @@
+--- a/rt3/step3/show_mpls_table.ref
++++ b/rt3/step4/show_mpls_table.ref
+@@ -7,23 +7,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.4.5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.5.5"
++ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+@@ -35,23 +19,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt5-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt5-2"
++ "interface":"eth-sw1"
+ }
+ ]
+ },
+@@ -63,23 +31,7 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.2",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.4.5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.5.5"
++ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+@@ -91,70 +43,6 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1",
+- "backupIndex":[
+- 0,
+- 1
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt5-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt5-2"
+- }
+- ]
+- },
+- "16040":{
+- "inLabel":16040,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "installed":true,
+- "nexthop":"10.0.5.5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "installed":true,
+- "nexthop":"10.0.4.5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "installed":true,
+- "nexthop":"10.0.1.2"
+- }
+- ]
+- },
+- "16041":{
+- "inLabel":16041,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "installed":true,
+- "interface":"eth-rt5-2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "installed":true,
+- "interface":"eth-rt5-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+@@ -167,26 +55,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.5.5",
+- "backupIndex":[
+- 0
+- ]
++ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.4.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.1.2"
++ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+@@ -198,26 +73,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5-2",
+- "backupIndex":[
+- 0
+- ]
++ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-sw1"
++ "interface":"eth-rt5-1"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..1af024f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ip_route.ref.diff
@@ -0,0 +1,288 @@
+--- a/rt3/step4/show_ip_route.ref
++++ b/rt3/step5/show_ip_route.ref
+@@ -15,10 +15,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.4.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-1",
++ "active":true,
++ "labels":[
++ 16040,
++ 16010
++ ]
++ },
++ {
++ "ip":"10.0.5.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-2",
++ "active":true,
++ "labels":[
++ 16040,
++ 16010
++ ]
++ }
+ ]
+ }
+ ],
+@@ -38,10 +64,36 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.4.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-1",
++ "active":true,
++ "labels":[
++ 16040,
++ 16020
++ ]
++ },
++ {
++ "ip":"10.0.5.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-2",
++ "active":true,
++ "labels":[
++ 16040,
++ 16020
++ ]
++ }
+ ]
+ }
+ ],
+@@ -60,21 +112,30 @@
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -95,6 +156,9 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -105,10 +169,25 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16040,
++ 16050
++ ]
++ }
+ ]
+ }
+ ],
+@@ -172,12 +251,40 @@
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
+ },
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-sw1"
++ "interfaceName":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.4.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-1",
++ "active":true,
++ "labels":[
++ 16040
++ ]
++ },
++ {
++ "ip":"10.0.5.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5-2",
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -268,13 +375,30 @@
+ {
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-1"
++ "interfaceName":"eth-rt5-1",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -290,12 +414,29 @@
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5-2"
++ "interfaceName":"eth-rt5-2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -390,14 +531,31 @@
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.1.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..7cc79d0
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,139 @@
+--- a/rt3/step4/show_ipv6_route.ref
++++ b/rt3/step5/show_ipv6_route.ref
+@@ -14,10 +14,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5-1",
++ "active":true,
++ "labels":[
++ 16041,
++ 16011
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5-2",
++ "active":true,
++ "labels":[
++ 16041,
++ 16011
++ ]
++ }
+ ]
+ }
+ ],
+@@ -36,10 +60,34 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+ "active":true,
++ "backupIndex":[
++ 0,
++ 1
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5-1",
++ "active":true,
++ "labels":[
++ 16041,
++ 16021
++ ]
++ },
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5-2",
++ "active":true,
++ "labels":[
++ 16041,
++ 16021
++ ]
++ }
+ ]
+ }
+ ],
+@@ -57,19 +105,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-sw1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ }
+ ]
+ }
+@@ -89,6 +146,9 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -98,10 +158,24 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-sw1",
++ "active":true,
++ "labels":[
++ 16041,
++ 16051
++ ]
++ }
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..75a0f01
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step5/show_mpls_table.ref.diff
@@ -0,0 +1,206 @@
+--- a/rt3/step4/show_mpls_table.ref
++++ b/rt3/step5/show_mpls_table.ref
+@@ -7,7 +7,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.1"
++ "nexthop":"10.0.1.1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.4.5"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.5.5"
+ }
+ ]
+ },
+@@ -19,7 +35,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-sw1"
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt5-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt5-2"
+ }
+ ]
+ },
+@@ -31,7 +63,23 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.1.2"
++ "nexthop":"10.0.1.2",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.4.5"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.5.5"
+ }
+ ]
+ },
+@@ -43,6 +91,70 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
++ "interface":"eth-sw1",
++ "backupIndex":[
++ 0,
++ 1
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt5-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt5-2"
++ }
++ ]
++ },
++ "16040":{
++ "inLabel":16040,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "installed":true,
++ "nexthop":"10.0.5.5"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "installed":true,
++ "nexthop":"10.0.4.5"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "installed":true,
++ "nexthop":"10.0.1.2"
++ }
++ ]
++ },
++ "16041":{
++ "inLabel":16041,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "installed":true,
++ "interface":"eth-rt5-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "installed":true,
++ "interface":"eth-rt5-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "installed":true,
+ "interface":"eth-sw1"
+ }
+ ]
+@@ -55,13 +167,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.5.5"
++ "nexthop":"10.0.5.5",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.4.5"
++ "nexthop":"10.0.4.5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+@@ -73,13 +198,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5-2"
++ "interface":"eth-rt5-2",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5-1"
++ "interface":"eth-rt5-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-sw1"
+ }
+ ]
+ },
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..c814a28
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ip_route.ref.diff
@@ -0,0 +1,101 @@
+--- a/rt3/step5/show_ip_route.ref
++++ b/rt3/step6/show_ip_route.ref
+@@ -31,7 +31,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16040,
++ 30040,
+ 16010
+ ]
+ },
+@@ -41,7 +41,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16040,
++ 30040,
+ 16010
+ ]
+ }
+@@ -80,7 +80,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16040,
++ 30040,
+ 16020
+ ]
+ },
+@@ -90,7 +90,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16040,
++ 30040,
+ 16020
+ ]
+ }
+@@ -124,7 +124,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16040
++ 30040
+ ]
+ },
+ {
+@@ -134,7 +134,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16040
++ 30040
+ ]
+ }
+ ]
+@@ -185,7 +185,7 @@
+ "active":true,
+ "labels":[
+ 16040,
+- 16050
++ 30050
+ ]
+ }
+ ]
+@@ -211,7 +211,7 @@
+ 0
+ ],
+ "labels":[
+- 16060
++ 30060
+ ]
+ },
+ {
+@@ -224,7 +224,7 @@
+ 0
+ ],
+ "labels":[
+- 16060
++ 30060
+ ]
+ }
+ ],
+@@ -274,7 +274,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16040
++ 30040
+ ]
+ },
+ {
+@@ -283,7 +283,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16040
++ 30040
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..6f9405f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,83 @@
+--- a/rt3/step5/show_ipv6_route.ref
++++ b/rt3/step6/show_ipv6_route.ref
+@@ -29,7 +29,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16041,
++ 30041,
+ 16011
+ ]
+ },
+@@ -38,7 +38,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16041,
++ 30041,
+ 16011
+ ]
+ }
+@@ -75,7 +75,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16041,
++ 30041,
+ 16021
+ ]
+ },
+@@ -84,7 +84,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16041,
++ 30041,
+ 16021
+ ]
+ }
+@@ -107,7 +107,7 @@
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+- 16041
++ 30041
+ ]
+ },
+ {
+@@ -125,7 +125,7 @@
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+- 16041
++ 30041
+ ]
+ }
+ ]
+@@ -173,7 +173,7 @@
+ "active":true,
+ "labels":[
+ 16041,
+- 16051
++ 30051
+ ]
+ }
+ ]
+@@ -198,7 +198,7 @@
+ 0
+ ],
+ "labels":[
+- 16061
++ 30061
+ ]
+ },
+ {
+@@ -210,7 +210,7 @@
+ 0
+ ],
+ "labels":[
+- 16061
++ 30061
+ ]
+ }
+ ],
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..d8c3968
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step6/show_mpls_table.ref.diff
@@ -0,0 +1,130 @@
+--- a/rt3/step5/show_mpls_table.ref
++++ b/rt3/step6/show_mpls_table.ref
+@@ -17,12 +17,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "nexthop":"10.0.5.5"
+ }
+ ]
+@@ -45,12 +45,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "interface":"eth-rt5-2"
+ }
+ ]
+@@ -73,12 +73,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "nexthop":"10.0.5.5"
+ }
+ ]
+@@ -101,12 +101,12 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "interface":"eth-rt5-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "interface":"eth-rt5-2"
+ }
+ ]
+@@ -117,13 +117,13 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ },
+@@ -141,13 +141,13 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "installed":true,
+ "interface":"eth-rt5-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "installed":true,
+ "interface":"eth-rt5-1"
+ },
+@@ -227,7 +227,7 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":30060,
+ "installed":true,
+ "nexthop":"10.0.5.5",
+ "backupIndex":[
+@@ -236,7 +236,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":30060,
+ "installed":true,
+ "nexthop":"10.0.4.5",
+ "backupIndex":[
+@@ -258,7 +258,7 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":30061,
+ "installed":true,
+ "interface":"eth-rt5-2",
+ "backupIndex":[
+@@ -267,7 +267,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":30061,
+ "installed":true,
+ "interface":"eth-rt5-1",
+ "backupIndex":[
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..c928fcd
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ip_route.ref.diff
@@ -0,0 +1,32 @@
+--- a/rt3/step6/show_ip_route.ref
++++ b/rt3/step7/show_ip_route.ref
+@@ -158,9 +158,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ },
+ {
+@@ -171,9 +168,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -184,8 +178,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16040,
+- 30050
++ 16040
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..0170971
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,32 @@
+--- a/rt3/step6/show_ipv6_route.ref
++++ b/rt3/step7/show_ipv6_route.ref
+@@ -148,9 +148,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ },
+ {
+@@ -160,9 +157,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -172,8 +166,7 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16041,
+- 30051
++ 16041
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..d7a3ed9
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step7/show_mpls_table.ref.diff
@@ -0,0 +1,71 @@
+--- a/rt3/step6/show_mpls_table.ref
++++ b/rt3/step7/show_mpls_table.ref
+@@ -159,68 +159,6 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.5.5",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.4.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.1.2"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-sw1"
+- }
+- ]
+- },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..41a7ff3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ip_route.ref.diff
@@ -0,0 +1,32 @@
+--- a/rt3/step7/show_ip_route.ref
++++ b/rt3/step8/show_ip_route.ref
+@@ -158,6 +158,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ },
+ {
+@@ -168,6 +171,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -178,7 +184,8 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16040
++ 16040,
++ 30050
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..bd49f86
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,32 @@
+--- a/rt3/step7/show_ipv6_route.ref
++++ b/rt3/step8/show_ipv6_route.ref
+@@ -148,6 +148,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ },
+ {
+@@ -157,6 +160,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -166,7 +172,8 @@
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+- 16041
++ 16041,
++ 30051
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..4cc69b6
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step8/show_mpls_table.ref.diff
@@ -0,0 +1,71 @@
+--- a/rt3/step7/show_mpls_table.ref
++++ b/rt3/step8/show_mpls_table.ref
+@@ -159,6 +159,68 @@
+ }
+ ]
+ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.5.5",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.4.5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.1.2"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt5-2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt5-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-sw1"
++ }
++ ]
++ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..cc0a482
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ip_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt3/step8/show_ip_route.ref
++++ b/rt3/step9/show_ip_route.ref
+@@ -185,7 +185,7 @@
+ "active":true,
+ "labels":[
+ 16040,
+- 30050
++ 30500
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..650b982
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt3/step8/show_ipv6_route.ref
++++ b/rt3/step9/show_ipv6_route.ref
+@@ -173,7 +173,7 @@
+ "active":true,
+ "labels":[
+ 16041,
+- 30051
++ 30501
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..8ce4f1d
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/step9/show_mpls_table.ref.diff
@@ -0,0 +1,133 @@
+--- a/rt3/step8/show_mpls_table.ref
++++ b/rt3/step9/show_mpls_table.ref
+@@ -159,13 +159,13 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
++ "16060":{
++ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":30060,
+ "installed":true,
+ "nexthop":"10.0.5.5",
+ "backupIndex":[
+@@ -174,7 +174,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":30060,
+ "installed":true,
+ "nexthop":"10.0.4.5",
+ "backupIndex":[
+@@ -185,18 +185,18 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":16060,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+- "16051":{
+- "inLabel":16051,
++ "16061":{
++ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":30061,
+ "installed":true,
+ "interface":"eth-rt5-2",
+ "backupIndex":[
+@@ -205,7 +205,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":30061,
+ "installed":true,
+ "interface":"eth-rt5-1",
+ "backupIndex":[
+@@ -216,18 +216,18 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":16061,
+ "interface":"eth-sw1"
+ }
+ ]
+ },
+- "16060":{
+- "inLabel":16060,
++ "16500":{
++ "inLabel":16500,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30060,
++ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.5.5",
+ "backupIndex":[
+@@ -236,7 +236,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30060,
++ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.4.5",
+ "backupIndex":[
+@@ -247,18 +247,18 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":16040,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+- "16061":{
+- "inLabel":16061,
++ "16501":{
++ "inLabel":16501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30061,
++ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5-2",
+ "backupIndex":[
+@@ -267,7 +267,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30061,
++ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5-1",
+ "backupIndex":[
+@@ -278,7 +278,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":16041,
+ "interface":"eth-sw1"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt3/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..441c9a3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt3/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+!
+interface eth-rt5-1
+ ip address 10.0.4.3/24
+!
+interface eth-rt5-2
+ ip address 10.0.5.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf
new file mode 100644
index 0000000..9223852
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/isisd.conf
@@ -0,0 +1,53 @@
+hostname rt4
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt2-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt2-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+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
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40
+ segment-routing prefix 2001:db8:1000::4/128 index 41
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000..0ef5d1b
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ip_route.ref
@@ -0,0 +1,506 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16010
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030,
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ },
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ },
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..b640df3
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_ipv6_route.ref
@@ -0,0 +1,207 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16011
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031,
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_mpls_table.ref
new file mode 100644
index 0000000..f60937c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_mpls_table.ref
@@ -0,0 +1,262 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt2-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.3.2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.2.2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt2-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt2-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt2-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.6.5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "interface":"eth-rt5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..2eb64b6
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,82 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt2-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt2-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step2/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step3/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..a941847
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ip_route.ref.diff
@@ -0,0 +1,367 @@
+--- a/rt4/step3/show_ip_route.ref
++++ b/rt4/step4/show_ip_route.ref
+@@ -14,37 +14,14 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 16010
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 16010
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16010
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -64,38 +41,14 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16030,
+- 16020
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -115,30 +68,21 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -158,24 +102,7 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -195,24 +122,7 @@
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16060
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -232,27 +142,13 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -268,30 +164,13 @@
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt2-1",
+- "backupIndex":[
+- 0
+- ]
++ "interfaceName":"eth-rt2-1"
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -307,29 +186,12 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt2-2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "interfaceName":"eth-rt2-2"
+ }
+ ]
+ }
+@@ -349,31 +211,6 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+- "ip":"10.0.2.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2-1",
+- "active":true
+- },
+- {
+- "ip":"10.0.3.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+@@ -394,31 +231,6 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0,
+- 1,
+- 2
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
+- },
+- {
+- "ip":"10.0.2.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2-1",
+- "active":true
+- },
+- {
+- "ip":"10.0.3.2",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+@@ -434,18 +246,7 @@
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.7.6",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "active":true
++ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+@@ -460,18 +261,7 @@
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.5",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt5",
+- "active":true
++ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..991562a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,161 @@
+--- a/rt4/step3/show_ipv6_route.ref
++++ b/rt4/step4/show_ipv6_route.ref
+@@ -13,35 +13,13 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 16011
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 16011
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16011
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -60,36 +38,13 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16031,
+- 16021
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -108,28 +63,19 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -148,23 +94,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -183,23 +113,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "backupIndex":[
+- 0
+- ],
+- "labels":[
+- 3
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16061
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..660d2fb
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step4/show_mpls_table.ref.diff
@@ -0,0 +1,265 @@
+--- a/rt4/step3/show_mpls_table.ref
++++ b/rt4/step4/show_mpls_table.ref
+@@ -1,262 +1,2 @@
+ {
+- "16010":{
+- "inLabel":16010,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "installed":true,
+- "nexthop":"10.0.3.2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "installed":true,
+- "nexthop":"10.0.2.2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "nexthop":"10.0.6.5"
+- }
+- ]
+- },
+- "16011":{
+- "inLabel":16011,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "installed":true,
+- "interface":"eth-rt2-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "installed":true,
+- "interface":"eth-rt2-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "interface":"eth-rt5"
+- }
+- ]
+- },
+- "16020":{
+- "inLabel":16020,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.3.2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.2.2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "nexthop":"10.0.6.5"
+- }
+- ]
+- },
+- "16021":{
+- "inLabel":16021,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt2-2",
+- "backupIndex":[
+- 0
+- ]
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt2-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "interface":"eth-rt5"
+- }
+- ]
+- },
+- "16030":{
+- "inLabel":16030,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.3.2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.2.2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16030,
+- "installed":true,
+- "nexthop":"10.0.6.5"
+- }
+- ]
+- },
+- "16031":{
+- "inLabel":16031,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt2-2"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt2-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "installed":true,
+- "interface":"eth-rt5"
+- }
+- ]
+- },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.6.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.7.6"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt6"
+- }
+- ]
+- },
+- "16060":{
+- "inLabel":16060,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.7.6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16060,
+- "nexthop":"10.0.6.5"
+- }
+- ]
+- },
+- "16061":{
+- "inLabel":16061,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt6",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16061,
+- "interface":"eth-rt5"
+- }
+- ]
+- }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..4385df2
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ip_route.ref.diff
@@ -0,0 +1,367 @@
+--- a/rt4/step4/show_ip_route.ref
++++ b/rt4/step5/show_ip_route.ref
+@@ -14,14 +14,37 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 16010
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 16010
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16010
++ ]
+ }
+ ]
+ }
+@@ -41,14 +64,38 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16030,
++ 16020
++ ]
+ }
+ ]
+ }
+@@ -68,21 +115,30 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ }
+ ]
+ }
+@@ -102,7 +158,24 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.7.6",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt6",
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
+@@ -122,7 +195,24 @@
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16060
++ ]
+ }
+ ]
+ }
+@@ -142,13 +232,27 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+@@ -164,13 +268,30 @@
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt2-1"
++ "interfaceName":"eth-rt2-1",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ }
+ ]
+ }
+@@ -186,12 +307,29 @@
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt2-2"
++ "interfaceName":"eth-rt2-2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ }
+ ]
+ }
+@@ -211,6 +349,31 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.7.6",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt6",
++ "active":true
++ },
++ {
++ "ip":"10.0.2.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2-1",
++ "active":true
++ },
++ {
++ "ip":"10.0.3.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+@@ -231,6 +394,31 @@
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
++ "active":true,
++ "backupIndex":[
++ 0,
++ 1,
++ 2
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.7.6",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt6",
++ "active":true
++ },
++ {
++ "ip":"10.0.2.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2-1",
++ "active":true
++ },
++ {
++ "ip":"10.0.3.2",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+@@ -246,7 +434,18 @@
+ {
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt5"
++ "interfaceName":"eth-rt5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.7.6",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt6",
++ "active":true
+ }
+ ]
+ }
+@@ -261,7 +460,18 @@
+ {
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt6"
++ "interfaceName":"eth-rt6",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.5",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt5",
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..54a1dc2
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,161 @@
+--- a/rt4/step4/show_ipv6_route.ref
++++ b/rt4/step5/show_ipv6_route.ref
+@@ -13,13 +13,35 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 16011
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 16011
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16011
++ ]
+ }
+ ]
+ }
+@@ -38,13 +60,36 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16031,
++ 16021
++ ]
+ }
+ ]
+ }
+@@ -63,19 +108,28 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-2",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2-1",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ }
+ ]
+ }
+@@ -94,7 +148,23 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt6",
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
+@@ -113,7 +183,23 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ],
++ "labels":[
++ 3
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt5",
++ "active":true,
++ "labels":[
++ 16061
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..fb6a119
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step5/show_mpls_table.ref.diff
@@ -0,0 +1,265 @@
+--- a/rt4/step4/show_mpls_table.ref
++++ b/rt4/step5/show_mpls_table.ref
+@@ -1,2 +1,262 @@
+ {
++ "16010":{
++ "inLabel":16010,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "installed":true,
++ "nexthop":"10.0.3.2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "installed":true,
++ "nexthop":"10.0.2.2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16011":{
++ "inLabel":16011,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "installed":true,
++ "interface":"eth-rt2-2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "installed":true,
++ "interface":"eth-rt2-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "interface":"eth-rt5"
++ }
++ ]
++ },
++ "16020":{
++ "inLabel":16020,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.3.2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.2.2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16021":{
++ "inLabel":16021,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt2-2",
++ "backupIndex":[
++ 0
++ ]
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt2-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "interface":"eth-rt5"
++ }
++ ]
++ },
++ "16030":{
++ "inLabel":16030,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.3.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.2.2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16030,
++ "installed":true,
++ "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16031":{
++ "inLabel":16031,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt2-2"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt2-1"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16031,
++ "installed":true,
++ "interface":"eth-rt5"
++ }
++ ]
++ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.6.5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.7.6"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt6"
++ }
++ ]
++ },
++ "16060":{
++ "inLabel":16060,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.7.6",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16060,
++ "nexthop":"10.0.6.5"
++ }
++ ]
++ },
++ "16061":{
++ "inLabel":16061,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt6",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16061,
++ "interface":"eth-rt5"
++ }
++ ]
++ }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..9070414
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ip_route.ref.diff
@@ -0,0 +1,56 @@
+--- a/rt4/step5/show_ip_route.ref
++++ b/rt4/step6/show_ip_route.ref
+@@ -43,7 +43,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16010
++ 30010
+ ]
+ }
+ ]
+@@ -93,7 +93,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16030,
++ 30030,
+ 16020
+ ]
+ }
+@@ -137,7 +137,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16030
++ 30030
+ ]
+ }
+ ]
+@@ -211,7 +211,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16060
++ 30060
+ ]
+ }
+ ]
+@@ -290,7 +290,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16030
++ 30030
+ ]
+ }
+ ]
+@@ -328,7 +328,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16030
++ 30030
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..57a5764
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,38 @@
+--- a/rt4/step5/show_ipv6_route.ref
++++ b/rt4/step6/show_ipv6_route.ref
+@@ -40,7 +40,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16011
++ 30011
+ ]
+ }
+ ]
+@@ -87,7 +87,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16031,
++ 30031,
+ 16021
+ ]
+ }
+@@ -128,7 +128,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16031
++ 30031
+ ]
+ }
+ ]
+@@ -198,7 +198,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16061
++ 30061
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..94f8785
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step6/show_mpls_table.ref.diff
@@ -0,0 +1,74 @@
+--- a/rt4/step5/show_mpls_table.ref
++++ b/rt4/step6/show_mpls_table.ref
+@@ -25,7 +25,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16010,
++ "outLabel":30010,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+@@ -56,7 +56,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16011,
++ "outLabel":30011,
+ "interface":"eth-rt5"
+ }
+ ]
+@@ -87,7 +87,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16030,
++ "outLabel":30030,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+@@ -118,7 +118,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16031,
++ "outLabel":30031,
+ "interface":"eth-rt5"
+ }
+ ]
+@@ -141,7 +141,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16030,
++ "outLabel":30030,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+@@ -165,7 +165,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16031,
++ "outLabel":30031,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+@@ -232,7 +232,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":30060,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+@@ -254,7 +254,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":30061,
+ "interface":"eth-rt5"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..e54873d
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ip_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt4/step6/show_ip_route.ref
++++ b/rt4/step7/show_ip_route.ref
+@@ -161,9 +161,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -172,10 +169,7 @@
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..92e08f9
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt4/step6/show_ipv6_route.ref
++++ b/rt4/step7/show_ipv6_route.ref
+@@ -151,9 +151,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -161,10 +158,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..fb614eb
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step7/show_mpls_table.ref.diff
@@ -0,0 +1,53 @@
+--- a/rt4/step6/show_mpls_table.ref
++++ b/rt4/step7/show_mpls_table.ref
+@@ -171,50 +171,6 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.6.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.7.6"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt6"
+- }
+- ]
+- },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..252da6e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ip_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt4/step7/show_ip_route.ref
++++ b/rt4/step8/show_ip_route.ref
+@@ -161,6 +161,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -169,7 +172,10 @@
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..7057d21
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt4/step7/show_ipv6_route.ref
++++ b/rt4/step8/show_ipv6_route.ref
+@@ -151,6 +151,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -158,7 +161,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..3dc4303
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step8/show_mpls_table.ref.diff
@@ -0,0 +1,53 @@
+--- a/rt4/step7/show_mpls_table.ref
++++ b/rt4/step8/show_mpls_table.ref
+@@ -171,6 +171,50 @@
+ }
+ ]
+ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.6.5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.7.6"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt6"
++ }
++ ]
++ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..56f9cc5
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ip_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt4/step8/show_ip_route.ref
++++ b/rt4/step9/show_ip_route.ref
+@@ -174,7 +174,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..41e5521
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt4/step8/show_ipv6_route.ref
++++ b/rt4/step9/show_ipv6_route.ref
+@@ -163,7 +163,7 @@
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..627e292
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/step9/show_mpls_table.ref.diff
@@ -0,0 +1,110 @@
+--- a/rt4/step8/show_mpls_table.ref
++++ b/rt4/step9/show_mpls_table.ref
+@@ -171,15 +171,15 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
++ "16060":{
++ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.6.5",
++ "nexthop":"10.0.7.6",
+ "backupIndex":[
+ 0
+ ]
+@@ -188,20 +188,20 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.7.6"
++ "outLabel":30060,
++ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+- "16051":{
+- "inLabel":16051,
++ "16061":{
++ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt5",
++ "interface":"eth-rt6",
+ "backupIndex":[
+ 0
+ ]
+@@ -210,20 +210,20 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt6"
++ "outLabel":30061,
++ "interface":"eth-rt5"
+ }
+ ]
+ },
+- "16060":{
+- "inLabel":16060,
++ "16500":{
++ "inLabel":16500,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.7.6",
++ "nexthop":"10.0.6.5",
+ "backupIndex":[
+ 0
+ ]
+@@ -232,20 +232,20 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30060,
+- "nexthop":"10.0.6.5"
++ "outLabel":16500,
++ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+- "16061":{
+- "inLabel":16061,
++ "16501":{
++ "inLabel":16501,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt6",
++ "interface":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+@@ -254,8 +254,8 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":30061,
+- "interface":"eth-rt5"
++ "outLabel":16501,
++ "interface":"eth-rt6"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt4/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..a2569aa
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt4/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt2-1
+ ip address 10.0.2.4/24
+!
+interface eth-rt2-2
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf
new file mode 100644
index 0000000..a08534c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/isisd.conf
@@ -0,0 +1,53 @@
+hostname rt5
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt3-1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt3-2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt6
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+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
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50
+ segment-routing prefix 2001:db8:1000::5/128 index 51
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000..93740e2
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,506 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16010
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020,
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ },
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0,
+ 1,
+ 2
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ },
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..6dafa69
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_ipv6_route.ref
@@ -0,0 +1,207 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16011
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16021,
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16061
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_mpls_table.ref
new file mode 100644
index 0000000..0c5861b
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_mpls_table.ref
@@ -0,0 +1,262 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.5.3",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.4.3",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt3-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt3-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.5.3",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.4.3",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt3-2",
+ "backupIndex":[
+ 0
+ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt3-1",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.6.4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16060,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16061":{
+ "inLabel":16061,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt6",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16061,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..1ff8c2c
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,82 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt3-1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt3-2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt6",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0006",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step2/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step3/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..7545a31
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ip_route.ref.diff
@@ -0,0 +1,161 @@
+--- a/rt5/step3/show_ip_route.ref
++++ b/rt5/step4/show_ip_route.ref
+@@ -41,10 +41,7 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16010
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -84,10 +81,7 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16020
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -108,9 +102,6 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -121,25 +112,10 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16020,
+- 16030
+- ]
+- }
+ ]
+ }
+ ],
+@@ -161,9 +137,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -172,10 +145,7 @@
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -209,10 +179,7 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16060
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -358,30 +325,13 @@
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt3-1",
+- "backupIndex":[
+- 0
+- ]
++ "interfaceName":"eth-rt3-1"
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16020
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -397,29 +347,12 @@
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+- "active":true,
+- "backupIndex":[
+- 0
+- ]
++ "active":true
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt3-2",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "ip":"10.0.6.4",
+- "afi":"ipv4",
+- "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16020
+- ]
++ "interfaceName":"eth-rt3-2"
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..1de62bb
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,95 @@
+--- a/rt5/step3/show_ipv6_route.ref
++++ b/rt5/step4/show_ipv6_route.ref
+@@ -38,10 +38,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16011
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -60,10 +57,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16021
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+@@ -101,9 +95,6 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+@@ -113,24 +104,10 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+- "backupIndex":[
+- 0
+- ],
+ "labels":[
+ 3
+ ]
+ }
+- ],
+- "backupNexthops":[
+- {
+- "afi":"ipv6",
+- "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16021,
+- 16031
+- ]
+- }
+ ]
+ }
+ ],
+@@ -151,9 +128,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -161,10 +135,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -196,10 +167,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16061
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..b3d5252
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step4/show_mpls_table.ref.diff
@@ -0,0 +1,166 @@
+--- a/rt5/step3/show_mpls_table.ref
++++ b/rt5/step4/show_mpls_table.ref
+@@ -25,7 +25,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16010,
++ "outLabel":3,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+@@ -56,7 +56,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16011,
++ "outLabel":3,
+ "interface":"eth-rt4"
+ }
+ ]
+@@ -76,12 +76,6 @@
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16020,
+- "installed":true,
+- "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+@@ -100,12 +94,6 @@
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt3-1"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16021,
+- "installed":true,
+- "interface":"eth-rt4"
+ }
+ ]
+ },
+@@ -117,26 +105,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.5.3",
+- "backupIndex":[
+- 0
+- ]
++ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.4.3",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16020,
+- "nexthop":"10.0.6.4"
++ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+@@ -148,70 +123,13 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt3-2",
+- "backupIndex":[
+- 0
+- ]
++ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt3-1",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16021,
+- "interface":"eth-rt4"
+- }
+- ]
+- },
+- "16040":{
+- "inLabel":16040,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.6.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.8.6"
+- }
+- ]
+- },
+- "16041":{
+- "inLabel":16041,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt6"
++ "interface":"eth-rt3-1"
+ }
+ ]
+ },
+@@ -232,7 +150,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16060,
++ "outLabel":3,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+@@ -254,7 +172,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16061,
++ "outLabel":3,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..be5d83f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ip_route.ref.diff
@@ -0,0 +1,161 @@
+--- a/rt5/step4/show_ip_route.ref
++++ b/rt5/step5/show_ip_route.ref
+@@ -41,7 +41,10 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16010
++ ]
+ }
+ ]
+ }
+@@ -81,7 +84,10 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16020
++ ]
+ }
+ ]
+ }
+@@ -102,6 +108,9 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -112,10 +121,25 @@
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4",
++ "active":true,
++ "labels":[
++ 16020,
++ 16030
++ ]
++ }
+ ]
+ }
+ ],
+@@ -137,6 +161,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -145,7 +172,10 @@
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -179,7 +209,10 @@
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16060
++ ]
+ }
+ ]
+ }
+@@ -325,13 +358,30 @@
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt3-1"
++ "interfaceName":"eth-rt3-1",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4",
++ "active":true,
++ "labels":[
++ 16020
++ ]
+ }
+ ]
+ }
+@@ -347,12 +397,29 @@
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+- "active":true
++ "active":true,
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+- "interfaceName":"eth-rt3-2"
++ "interfaceName":"eth-rt3-2",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "ip":"10.0.6.4",
++ "afi":"ipv4",
++ "interfaceName":"eth-rt4",
++ "active":true,
++ "labels":[
++ 16020
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..a856019
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,95 @@
+--- a/rt5/step4/show_ipv6_route.ref
++++ b/rt5/step5/show_ipv6_route.ref
+@@ -38,7 +38,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16011
++ ]
+ }
+ ]
+ }
+@@ -57,7 +60,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16021
++ ]
+ },
+ {
+ "fib":true,
+@@ -95,6 +101,9 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+@@ -104,10 +113,24 @@
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
++ "backupIndex":[
++ 0
++ ],
+ "labels":[
+ 3
+ ]
+ }
++ ],
++ "backupNexthops":[
++ {
++ "afi":"ipv6",
++ "interfaceName":"eth-rt4",
++ "active":true,
++ "labels":[
++ 16021,
++ 16031
++ ]
++ }
+ ]
+ }
+ ],
+@@ -128,6 +151,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -135,7 +161,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ }
+ ]
+ }
+@@ -167,7 +196,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16061
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..74caa86
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step5/show_mpls_table.ref.diff
@@ -0,0 +1,166 @@
+--- a/rt5/step4/show_mpls_table.ref
++++ b/rt5/step5/show_mpls_table.ref
+@@ -25,7 +25,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16010,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+@@ -56,7 +56,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16011,
+ "interface":"eth-rt4"
+ }
+ ]
+@@ -69,6 +69,12 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
++ "nexthop":"10.0.6.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16020,
++ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+@@ -87,6 +93,12 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
++ "interface":"eth-rt4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16021,
++ "installed":true,
+ "interface":"eth-rt3-2"
+ },
+ {
+@@ -105,13 +117,26 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.5.3"
++ "nexthop":"10.0.5.3",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "nexthop":"10.0.4.3"
++ "nexthop":"10.0.4.3",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16020,
++ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+@@ -123,13 +148,70 @@
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt3-2"
++ "interface":"eth-rt3-2",
++ "backupIndex":[
++ 0
++ ]
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+- "interface":"eth-rt3-1"
++ "interface":"eth-rt3-1",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16021,
++ "interface":"eth-rt4"
++ }
++ ]
++ },
++ "16040":{
++ "inLabel":16040,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.6.4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.8.6"
++ }
++ ]
++ },
++ "16041":{
++ "inLabel":16041,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt6"
+ }
+ ]
+ },
+@@ -150,7 +232,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16060,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+@@ -172,7 +254,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16061,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..2883c04
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step6/show_mpls_table.ref.diff
@@ -0,0 +1,146 @@
+--- a/rt5/step5/show_mpls_table.ref
++++ b/rt5/step6/show_mpls_table.ref
+@@ -1,6 +1,6 @@
+ {
+- "16010":{
+- "inLabel":16010,
++ "30010":{
++ "inLabel":30010,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -30,8 +30,8 @@
+ }
+ ]
+ },
+- "16011":{
+- "inLabel":16011,
++ "30011":{
++ "inLabel":30011,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -61,56 +61,56 @@
+ }
+ ]
+ },
+- "16020":{
+- "inLabel":16020,
++ "30020":{
++ "inLabel":30020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+- "nexthop":"10.0.6.4"
++ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+- "nexthop":"10.0.5.3"
++ "nexthop":"10.0.4.3"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+- "nexthop":"10.0.4.3"
++ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+- "16021":{
+- "inLabel":16021,
++ "30021":{
++ "inLabel":30021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+- "interface":"eth-rt4"
++ "interface":"eth-rt3-2"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+- "interface":"eth-rt3-2"
++ "interface":"eth-rt3-1"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+- "interface":"eth-rt3-1"
++ "interface":"eth-rt4"
+ }
+ ]
+ },
+- "16030":{
+- "inLabel":16030,
++ "30030":{
++ "inLabel":30030,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -140,8 +140,8 @@
+ }
+ ]
+ },
+- "16031":{
+- "inLabel":16031,
++ "30031":{
++ "inLabel":30031,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -171,8 +171,8 @@
+ }
+ ]
+ },
+- "16040":{
+- "inLabel":16040,
++ "30040":{
++ "inLabel":30040,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -193,8 +193,8 @@
+ }
+ ]
+ },
+- "16041":{
+- "inLabel":16041,
++ "30041":{
++ "inLabel":30041,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -215,8 +215,8 @@
+ }
+ ]
+ },
+- "16060":{
+- "inLabel":16060,
++ "30060":{
++ "inLabel":30060,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -237,8 +237,8 @@
+ }
+ ]
+ },
+- "16061":{
+- "inLabel":16061,
++ "30061":{
++ "inLabel":30061,
+ "installed":true,
+ "nexthops":[
+ {
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step7/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step8/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/step9/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt5/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..f62cc8f
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt5/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt3-1
+ ip address 10.0.4.5/24
+!
+interface eth-rt3-2
+ ip address 10.0.5.5/24
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf
new file mode 100644
index 0000000..d92f822
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/isisd.conf
@@ -0,0 +1,39 @@
+hostname rt6
+log file isisd.log
+!
+! debug isis events
+! debug isis route-events
+! debug isis spf-events
+! debug isis sr-events
+! debug isis lsp-gen
+!
+interface lo
+ ip router isis 1
+ ipv6 router isis 1
+ isis passive
+!
+interface eth-rt4
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+interface eth-rt5
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis hello-multiplier 3
+ isis fast-reroute ti-lfa
+!
+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
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60
+ segment-routing prefix 2001:db8:1000::6/128 index 61
+!
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ip_route.ref b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000..b9b43c4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,413 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16020
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16030
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref
new file mode 100644
index 0000000..1b19429
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_ipv6_route.ref
@@ -0,0 +1,173 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ },
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16011
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16021
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16021
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 16031
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16031
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16041
+ ]
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"isis",
+ "selected":true,
+ "destSelected":true,
+ "distance":115,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "backupIndex":[
+ 0
+ ],
+ "labels":[
+ 3
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16051
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_mpls_table.ref b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_mpls_table.ref
new file mode 100644
index 0000000..5b52a16
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_mpls_table.ref
@@ -0,0 +1,214 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16011":{
+ "inLabel":16011,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+ },
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16020,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16021":{
+ "inLabel":16021,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "installed":true,
+ "interface":"eth-rt4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16021,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16030,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16031":{
+ "inLabel":16031,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "installed":true,
+ "interface":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16031,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16040,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16041":{
+ "inLabel":16041,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt4",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16041,
+ "interface":"eth-rt5"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16050,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16051":{
+ "inLabel":16051,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":3,
+ "installed":true,
+ "interface":"eth-rt5",
+ "backupIndex":[
+ 0
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+ "outLabel":16051,
+ "interface":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..7348323
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,44 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "eth-rt4",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0004",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "eth-rt5",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0005",
+ "hold-timer": 9,
+ "neighbor-priority": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step2/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step2/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ip_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ip_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_ipv6_route.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step3/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_mpls_table.ref.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step3/show_mpls_table.ref.diff
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ip_route.ref.diff
new file mode 100644
index 0000000..7c2f004
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ip_route.ref.diff
@@ -0,0 +1,70 @@
+--- a/rt6/step3/show_ip_route.ref
++++ b/rt6/step4/show_ip_route.ref
+@@ -14,10 +14,7 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16010
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+@@ -50,9 +47,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16020
+ ]
+ }
+ ],
+@@ -98,10 +92,7 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16030
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -124,9 +115,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -135,10 +123,7 @@
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16040
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -172,10 +157,7 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..70f872e
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_ipv6_route.ref.diff
@@ -0,0 +1,70 @@
+--- a/rt6/step3/show_ipv6_route.ref
++++ b/rt6/step4/show_ipv6_route.ref
+@@ -13,10 +13,7 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16011
+- ]
++ "active":true
+ },
+ {
+ "fib":true,
+@@ -47,9 +44,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 16021
+ ]
+ }
+ ],
+@@ -92,10 +86,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16031
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -117,9 +108,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -127,10 +115,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true,
+- "labels":[
+- 16041
+- ]
++ "active":true
+ }
+ ]
+ }
+@@ -162,10 +147,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step4/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_mpls_table.ref.diff
new file mode 100644
index 0000000..c191763
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step4/show_mpls_table.ref.diff
@@ -0,0 +1,109 @@
+--- a/rt6/step3/show_mpls_table.ref
++++ b/rt6/step4/show_mpls_table.ref
+@@ -8,12 +8,6 @@
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16010,
+- "installed":true,
+- "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+@@ -26,12 +20,6 @@
+ "outLabel":16011,
+ "installed":true,
+ "interface":"eth-rt5"
+- },
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16011,
+- "installed":true,
+- "interface":"eth-rt4"
+ }
+ ]
+ },
+@@ -96,7 +84,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16030,
++ "outLabel":3,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -118,52 +106,8 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16031,
+- "interface":"eth-rt4"
+- }
+- ]
+- },
+- "16040":{
+- "inLabel":16040,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.7.4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16040,
+- "nexthop":"10.0.8.5"
+- }
+- ]
+- },
+- "16041":{
+- "inLabel":16041,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+ "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt4",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16041,
+- "interface":"eth-rt5"
++ "interface":"eth-rt4"
+ }
+ ]
+ },
+@@ -184,7 +128,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":3,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -206,7 +150,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":3,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ip_route.ref.diff
new file mode 100644
index 0000000..9f017d2
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ip_route.ref.diff
@@ -0,0 +1,70 @@
+--- a/rt6/step4/show_ip_route.ref
++++ b/rt6/step5/show_ip_route.ref
+@@ -14,7 +14,10 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16010
++ ]
+ },
+ {
+ "fib":true,
+@@ -47,6 +50,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16020
+ ]
+ }
+ ],
+@@ -92,7 +98,10 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16030
++ ]
+ }
+ ]
+ }
+@@ -115,6 +124,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -123,7 +135,10 @@
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "labels":[
++ 16040
++ ]
+ }
+ ]
+ }
+@@ -157,7 +172,10 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..1209504
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_ipv6_route.ref.diff
@@ -0,0 +1,70 @@
+--- a/rt6/step4/show_ipv6_route.ref
++++ b/rt6/step5/show_ipv6_route.ref
+@@ -13,7 +13,10 @@
+ "fib":true,
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16011
++ ]
+ },
+ {
+ "fib":true,
+@@ -44,6 +47,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 16021
+ ]
+ }
+ ],
+@@ -86,7 +92,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16031
++ ]
+ }
+ ]
+ }
+@@ -108,6 +117,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -115,7 +127,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+- "active":true
++ "active":true,
++ "labels":[
++ 16041
++ ]
+ }
+ ]
+ }
+@@ -147,7 +162,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step5/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_mpls_table.ref.diff
new file mode 100644
index 0000000..abf7c2a
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step5/show_mpls_table.ref.diff
@@ -0,0 +1,112 @@
+--- a/rt6/step4/show_mpls_table.ref
++++ b/rt6/step5/show_mpls_table.ref
+@@ -7,6 +7,12 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16010,
+ "installed":true,
++ "nexthop":"10.0.7.4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16010,
++ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+@@ -19,6 +25,12 @@
+ "type":"SR (IS-IS)",
+ "outLabel":16011,
+ "installed":true,
++ "interface":"eth-rt4"
++ },
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16011,
++ "installed":true,
+ "interface":"eth-rt5"
+ }
+ ]
+@@ -84,7 +96,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16030,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -106,11 +118,55 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16031,
+ "interface":"eth-rt4"
+ }
+ ]
+ },
++ "16040":{
++ "inLabel":16040,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.7.4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16040,
++ "nexthop":"10.0.8.5"
++ }
++ ]
++ },
++ "16041":{
++ "inLabel":16041,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt4",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16041,
++ "interface":"eth-rt5"
++ }
++ ]
++ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+@@ -128,7 +184,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16050,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+@@ -150,7 +206,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":3,
++ "outLabel":16051,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ip_route.ref.diff
new file mode 100644
index 0000000..f318f95
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ip_route.ref.diff
@@ -0,0 +1,38 @@
+--- a/rt6/step5/show_ip_route.ref
++++ b/rt6/step6/show_ip_route.ref
+@@ -26,7 +26,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16010
++ 30010
+ ]
+ }
+ ]
+@@ -63,7 +63,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16020
++ 30020
+ ]
+ }
+ ]
+@@ -89,7 +89,7 @@
+ 0
+ ],
+ "labels":[
+- 16030
++ 30030
+ ]
+ }
+ ],
+@@ -137,7 +137,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16040
++ 30040
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..9208491
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_ipv6_route.ref.diff
@@ -0,0 +1,38 @@
+--- a/rt6/step5/show_ipv6_route.ref
++++ b/rt6/step6/show_ipv6_route.ref
+@@ -24,7 +24,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16011
++ 30011
+ ]
+ }
+ ]
+@@ -59,7 +59,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16021
++ 30021
+ ]
+ }
+ ]
+@@ -84,7 +84,7 @@
+ 0
+ ],
+ "labels":[
+- 16031
++ 30031
+ ]
+ }
+ ],
+@@ -129,7 +129,7 @@
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+- 16041
++ 30041
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step6/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_mpls_table.ref.diff
new file mode 100644
index 0000000..aee8969
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step6/show_mpls_table.ref.diff
@@ -0,0 +1,74 @@
+--- a/rt6/step5/show_mpls_table.ref
++++ b/rt6/step6/show_mpls_table.ref
+@@ -11,7 +11,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16010,
++ "outLabel":30010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+@@ -29,7 +29,7 @@
+ },
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16011,
++ "outLabel":30011,
+ "installed":true,
+ "interface":"eth-rt5"
+ }
+@@ -52,7 +52,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16020,
++ "outLabel":30020,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+@@ -74,7 +74,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16021,
++ "outLabel":30021,
+ "interface":"eth-rt5"
+ }
+ ]
+@@ -85,7 +85,7 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16030,
++ "outLabel":30030,
+ "installed":true,
+ "nexthop":"10.0.8.5",
+ "backupIndex":[
+@@ -107,7 +107,7 @@
+ "nexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16031,
++ "outLabel":30031,
+ "installed":true,
+ "interface":"eth-rt5",
+ "backupIndex":[
+@@ -140,7 +140,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16040,
++ "outLabel":30040,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+@@ -162,7 +162,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16041,
++ "outLabel":30041,
+ "interface":"eth-rt5"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ip_route.ref.diff
new file mode 100644
index 0000000..0e6c3ff
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ip_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt6/step6/show_ip_route.ref
++++ b/rt6/step7/show_ip_route.ref
+@@ -161,9 +161,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -172,10 +169,7 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16050
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..2fe46c8
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_ipv6_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt6/step6/show_ipv6_route.ref
++++ b/rt6/step7/show_ipv6_route.ref
+@@ -152,9 +152,6 @@
+ "active":true,
+ "backupIndex":[
+ 0
+- ],
+- "labels":[
+- 3
+ ]
+ }
+ ],
+@@ -162,10 +159,7 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true,
+- "labels":[
+- 16051
+- ]
++ "active":true
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step7/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_mpls_table.ref.diff
new file mode 100644
index 0000000..179a4f4
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step7/show_mpls_table.ref.diff
@@ -0,0 +1,52 @@
+--- a/rt6/step6/show_mpls_table.ref
++++ b/rt6/step7/show_mpls_table.ref
+@@ -166,49 +166,5 @@
+ "interface":"eth-rt5"
+ }
+ ]
+- },
+- "16050":{
+- "inLabel":16050,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "nexthop":"10.0.8.5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16050,
+- "nexthop":"10.0.7.4"
+- }
+- ]
+- },
+- "16051":{
+- "inLabel":16051,
+- "installed":true,
+- "nexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":3,
+- "installed":true,
+- "interface":"eth-rt5",
+- "backupIndex":[
+- 0
+- ]
+- }
+- ],
+- "backupNexthops":[
+- {
+- "type":"SR (IS-IS)",
+- "outLabel":16051,
+- "interface":"eth-rt4"
+- }
+- ]
+ }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ip_route.ref.diff
new file mode 100644
index 0000000..9d5c440
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ip_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt6/step7/show_ip_route.ref
++++ b/rt6/step8/show_ip_route.ref
+@@ -161,6 +161,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -169,7 +172,10 @@
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16050
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..21cab20
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_ipv6_route.ref.diff
@@ -0,0 +1,24 @@
+--- a/rt6/step7/show_ipv6_route.ref
++++ b/rt6/step8/show_ipv6_route.ref
+@@ -152,6 +152,9 @@
+ "active":true,
+ "backupIndex":[
+ 0
++ ],
++ "labels":[
++ 3
+ ]
+ }
+ ],
+@@ -159,7 +162,10 @@
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+- "active":true
++ "active":true,
++ "labels":[
++ 16051
++ ]
+ }
+ ]
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step8/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_mpls_table.ref.diff
new file mode 100644
index 0000000..760c554
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step8/show_mpls_table.ref.diff
@@ -0,0 +1,52 @@
+--- a/rt6/step7/show_mpls_table.ref
++++ b/rt6/step8/show_mpls_table.ref
+@@ -166,5 +166,49 @@
+ "interface":"eth-rt5"
+ }
+ ]
++ },
++ "16050":{
++ "inLabel":16050,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "nexthop":"10.0.8.5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16050,
++ "nexthop":"10.0.7.4"
++ }
++ ]
++ },
++ "16051":{
++ "inLabel":16051,
++ "installed":true,
++ "nexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":3,
++ "installed":true,
++ "interface":"eth-rt5",
++ "backupIndex":[
++ 0
++ ]
++ }
++ ],
++ "backupNexthops":[
++ {
++ "type":"SR (IS-IS)",
++ "outLabel":16051,
++ "interface":"eth-rt4"
++ }
++ ]
+ }
+ }
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ip_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ip_route.ref.diff
new file mode 100644
index 0000000..ee29647
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ip_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt6/step8/show_ip_route.ref
++++ b/rt6/step9/show_ip_route.ref
+@@ -174,7 +174,7 @@
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+- 16050
++ 16500
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ipv6_route.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ipv6_route.ref.diff
new file mode 100644
index 0000000..bebca4d
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_ipv6_route.ref.diff
@@ -0,0 +1,11 @@
+--- a/rt6/step8/show_ipv6_route.ref
++++ b/rt6/step9/show_ipv6_route.ref
+@@ -164,7 +164,7 @@
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+- 16051
++ 16501
+ ]
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/step9/show_mpls_table.ref.diff b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_mpls_table.ref.diff
new file mode 100644
index 0000000..57347d1
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/step9/show_mpls_table.ref.diff
@@ -0,0 +1,39 @@
+--- a/rt6/step8/show_mpls_table.ref
++++ b/rt6/step9/show_mpls_table.ref
+@@ -167,8 +167,8 @@
+ }
+ ]
+ },
+- "16050":{
+- "inLabel":16050,
++ "16500":{
++ "inLabel":16500,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -184,13 +184,13 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16050,
++ "outLabel":16500,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+- "16051":{
+- "inLabel":16051,
++ "16501":{
++ "inLabel":16501,
+ "installed":true,
+ "nexthops":[
+ {
+@@ -206,7 +206,7 @@
+ "backupNexthops":[
+ {
+ "type":"SR (IS-IS)",
+- "outLabel":16051,
++ "outLabel":16501,
+ "interface":"eth-rt4"
+ }
+ ]
diff --git a/tests/topotests/isis_tilfa_topo1/rt6/zebra.conf b/tests/topotests/isis_tilfa_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..4d51d3d
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
new file mode 100755
index 0000000..07e91f1
--- /dev/null
+++ b/tests/topotests/isis_tilfa_topo1/test_isis_tilfa_topo1.py
@@ -0,0 +1,755 @@
+#!/usr/bin/env python
+
+#
+# test_isis_tilfa_topo1.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_isis_tilfa_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
+ | | | |
+ 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
+ | | | |
+ eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+import tempfile
+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.
+
+pytestmark = [pytest.mark.isisd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+ #
+ # Populate multi-dimensional dictionary containing all expected outputs
+ #
+ files = [
+ "show_ip_route.ref",
+ "show_ipv6_route.ref",
+ "show_mpls_table.ref",
+ "show_yang_interface_isis_adjacencies.ref",
+ ]
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ outputs[rname] = {}
+ for step in range(1, 9 + 1):
+ outputs[rname][step] = {}
+ for file in files:
+ if step == 1:
+ # Get snapshots relative to the expected initial network convergence
+ filename = "{}/{}/step{}/{}".format(CWD, rname, step, file)
+ outputs[rname][step][file] = open(filename).read()
+ else:
+ if file == "show_yang_interface_isis_adjacencies.ref":
+ continue
+
+ # Get diff relative to the previous step
+ filename = "{}/{}/step{}/{}.diff".format(CWD, rname, step, file)
+
+ # Create temporary files in order to apply the diff
+ f_in = tempfile.NamedTemporaryFile(mode="w")
+ f_in.write(outputs[rname][step - 1][file])
+ f_in.flush()
+ f_out = tempfile.NamedTemporaryFile(mode="r")
+ os.system(
+ "patch -s -o %s %s %s" % (f_out.name, f_in.name, filename)
+ )
+
+ # Store the updated snapshot and remove the temporary files
+ outputs[rname][step][file] = open(f_out.name).read()
+ f_in.close()
+ f_out.close()
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ expected = json.loads(reference)
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_isis_adjacencies_step1():
+ logger.info("Test (step 1): check IS-IS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ outputs[rname][1]["show_yang_interface_isis_adjacencies.ref"],
+ )
+
+
+def test_rib_ipv4_step1():
+ logger.info("Test (step 1): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][1]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step1():
+ logger.info("Test (step 1): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][1]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step1():
+ logger.info("Test (step 1): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][1]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable TI-LFA link protection on rt2's eth-sw1 interface
+#
+# Expected changes:
+# -rt2 should uninstall the backup nexthops from destinations reachable over eth-sw1.
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling TI-LFA link protection on rt2's eth-sw1 interface")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-sw1" -c "no isis fast-reroute ti-lfa"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][2]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step2():
+ logger.info("Test (step 2): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][2]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step2():
+ logger.info("Test (step 2): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][2]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Enable TI-LFA link protection on rt2's eth-sw1 interface
+#
+# Expected changes:
+# -rt2 should install backup nexthops for destinations reachable over eth-sw1.
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling TI-LFA link protection on rt2's eth-sw1 interface")
+ tgen.net["rt2"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-sw1" -c "isis fast-reroute ti-lfa"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][3]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step3():
+ logger.info("Test (step 3): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][3]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step3():
+ logger.info("Test (step 3): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][3]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Disable SR on rt4
+#
+# Expected changes:
+# -rt4 should uninstall all Prefix-SIDs from the network
+# -rt4 should uninstall all TI-LFA backup nexthops
+# -All routers should uninstall rt4's Prefix-SIDs
+# -All routers should uninstall all SR labels for destinations whose nexthop is rt4
+# -All routers should uninstall all TI-LFA backup nexthops that point to rt4
+# -All routers should uninstall all TI-LFA backup nexthops that use rt4's Prefix-SIDs
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling SR on rt4")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing on"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][4]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step4():
+ logger.info("Test (step 4): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][4]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step4():
+ logger.info("Test (step 4): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][4]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Enable SR on rt4
+#
+# Expected changes:
+# -Reverse all changes done on the previous step
+#
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling SR on rt4")
+ tgen.net["rt4"].cmd('vtysh -c "conf t" -c "router isis 1" -c "segment-routing on"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][5]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step5():
+ logger.info("Test (step 5): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][5]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step5():
+ logger.info("Test (step 5): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][5]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Change rt5's SRGB
+#
+# Expected changes:
+# -All routers should update all SR labels for destinations whose primary or backup nexthop is rt5
+#
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Changing rt5's SRGB")
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing global-block 30000 37999"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][6]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step6():
+ logger.info("Test (step 6): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][6]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step6():
+ logger.info("Test (step 6): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][6]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Delete rt5's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should uninstall rt5's Prefix-SIDs
+# -All routers should uninstall all TI-LFA backup nexthops that use rt5's Prefix-SIDs
+#
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Deleting rt5's Prefix-SIDs")
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 5.5.5.5/32 index 50"'
+ )
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "no segment-routing prefix 2001:db8:1000::5/128 index 51"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][7]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step7():
+ logger.info("Test (step 7): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][7]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step7():
+ logger.info("Test (step 7): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][7]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Re-add rt5's Prefix-SIDs
+#
+# Expected changes:
+# -Reverse all changes done on the previous step
+#
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-adding rt5's Prefix-SIDs")
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 50"'
+ )
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 51"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][8]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step8():
+ logger.info("Test (step 8): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][8]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step8():
+ logger.info("Test (step 8): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][8]["show_mpls_table.ref"]
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Change rt5's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should update rt5's Prefix-SIDs
+# -All routers should update all TI-LFA backup nexthops that use rt5's Prefix-SIDs
+#
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-adding rt5's Prefix-SIDs")
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 5.5.5.5/32 index 500"'
+ )
+ tgen.net["rt5"].cmd(
+ 'vtysh -c "conf t" -c "router isis 1" -c "segment-routing prefix 2001:db8:1000::5/128 index 501"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route isis json", outputs[rname][9]["show_ip_route.ref"]
+ )
+
+
+def test_rib_ipv6_step9():
+ logger.info("Test (step 9): verify IPv6 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ipv6 route isis json", outputs[rname][9]["show_ipv6_route.ref"]
+ )
+
+
+def test_mpls_lib_step9():
+ logger.info("Test (step 9): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", outputs[rname][9]["show_mpls_table.ref"]
+ )
+
+
+# Memory leak test template
+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/isis_topo1/__init__.py b/tests/topotests/isis_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_topo1/__init__.py
diff --git a/tests/topotests/isis_topo1/r1/isisd.conf b/tests/topotests/isis_topo1/r1/isisd.conf
new file mode 100644
index 0000000..9c1bfff
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/isisd.conf
@@ -0,0 +1,17 @@
+hostname r1
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r1-eth0
+ ip router isis 1
+ isis hello-interval 2
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00
+ metric-style wide
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1/r1/r1_route.json b/tests/topotests/isis_topo1/r1/r1_route.json
new file mode 100644
index 0000000..f94233a
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/r1_route.json
@@ -0,0 +1,68 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r1-eth0",
+ "ip": "10.0.20.1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.1/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "10.254.0.1/32",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.3/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r1-eth0",
+ "ip": "10.0.20.1"
+ }
+ ],
+ "prefix": "10.254.0.3/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r1/r1_route6.json b/tests/topotests/isis_topo1/r1/r1_route6.json
new file mode 100644
index 0000000..bd09839
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/r1_route6.json
@@ -0,0 +1,66 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::1/128": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "2001:db8:f::1/128",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::3/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "2001:db8:f::3/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r1/r1_route6_linux.json b/tests/topotests/isis_topo1/r1/r1_route6_linux.json
new file mode 100644
index 0000000..139747c
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/r1_route6_linux.json
@@ -0,0 +1,14 @@
+{
+ "2001:db8:2:1::/64": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::3": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r1/r1_route_linux.json b/tests/topotests/isis_topo1/r1/r1_route_linux.json
new file mode 100644
index 0000000..6420dec
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/r1_route_linux.json
@@ -0,0 +1,14 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.20.1"
+ },
+ "10.254.0.3": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.20.1"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r1/r1_topology.json b/tests/topotests/isis_topo1/r1/r1_topology.json
new file mode 100644
index 0000000..337d6bf
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/r1_topology.json
@@ -0,0 +1,96 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r1"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r1"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r1"
+ },
+ {
+ "metric": "0",
+ "parent": "r1(4)",
+ "type": "IP internal",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r1(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.3/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r1"
+ },
+ {
+ "metric": "0",
+ "parent": "r1(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r1(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "metric": "10",
+ "interface": "r1-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r1-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::3/128"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1/r1/zebra.conf b/tests/topotests/isis_topo1/r1/zebra.conf
new file mode 100644
index 0000000..23cf625
--- /dev/null
+++ b/tests/topotests/isis_topo1/r1/zebra.conf
@@ -0,0 +1,9 @@
+hostname r1
+interface r1-eth0
+ ip address 10.0.20.2/24
+ ipv6 address 2001:db8:1:1::2/64
+!
+interface lo
+ ip address 10.254.0.1/32
+ ipv6 address 2001:db8:F::1/128
+!
diff --git a/tests/topotests/isis_topo1/r2/isisd.conf b/tests/topotests/isis_topo1/r2/isisd.conf
new file mode 100644
index 0000000..e8b5789
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/isisd.conf
@@ -0,0 +1,17 @@
+hostname r2
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r2-eth0
+ ip router isis 1
+ isis hello-interval 2
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1/r2/r2_route.json b/tests/topotests/isis_topo1/r2/r2_route.json
new file mode 100644
index 0000000..aab651e
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/r2_route.json
@@ -0,0 +1,68 @@
+{
+ "10.0.11.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r2-eth0",
+ "ip": "10.0.21.1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.2/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "10.254.0.2/32",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.4/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r2-eth0",
+ "ip": "10.0.21.1"
+ }
+ ],
+ "prefix": "10.254.0.4/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r2/r2_route6.json b/tests/topotests/isis_topo1/r2/r2_route6.json
new file mode 100644
index 0000000..78c31b3
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/r2_route6.json
@@ -0,0 +1,66 @@
+{
+ "2001:db8:1:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::2/128": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "2001:db8:f::2/128",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::4/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "2001:db8:f::4/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r2/r2_route6_linux.json b/tests/topotests/isis_topo1/r2/r2_route6_linux.json
new file mode 100644
index 0000000..5068861
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/r2_route6_linux.json
@@ -0,0 +1,14 @@
+{
+ "2001:db8:2:2::/64": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::4": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r2/r2_route_linux.json b/tests/topotests/isis_topo1/r2/r2_route_linux.json
new file mode 100644
index 0000000..dd3035a
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/r2_route_linux.json
@@ -0,0 +1,14 @@
+{
+ "10.0.11.0/24": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.21.1"
+ },
+ "10.254.0.4": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.21.1"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r2/r2_topology.json b/tests/topotests/isis_topo1/r2/r2_topology.json
new file mode 100644
index 0000000..de90fb5
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/r2_topology.json
@@ -0,0 +1,96 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r2"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r2"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r2"
+ },
+ {
+ "metric": "0",
+ "parent": "r2(4)",
+ "type": "IP internal",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r2(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.4/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r2"
+ },
+ {
+ "metric": "0",
+ "parent": "r2(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r2(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "metric": "10",
+ "interface": "r2-eth0",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r2-eth0",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::4/128"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1/r2/zebra.conf b/tests/topotests/isis_topo1/r2/zebra.conf
new file mode 100644
index 0000000..cf6f8f6
--- /dev/null
+++ b/tests/topotests/isis_topo1/r2/zebra.conf
@@ -0,0 +1,9 @@
+hostname r2
+interface r2-eth0
+ ip address 10.0.21.2/24
+ ipv6 address 2001:db8:1:2::2/64
+!
+interface lo
+ ip address 10.254.0.2/32
+ ipv6 address 2001:db8:F::2/128
+!
diff --git a/tests/topotests/isis_topo1/r3/isisd.conf b/tests/topotests/isis_topo1/r3/isisd.conf
new file mode 100644
index 0000000..47bfc53
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/isisd.conf
@@ -0,0 +1,24 @@
+hostname r3
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r3-eth0
+ ip router isis 1
+ isis hello-interval 2
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-1
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1/r3/r3_route.json b/tests/topotests/isis_topo1/r3/r3_route.json
new file mode 100644
index 0000000..61d05e8
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_route.json
@@ -0,0 +1,150 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.0.11.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth0"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.1/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth0",
+ "ip": "10.0.20.2"
+ }
+ ],
+ "prefix": "10.254.0.1/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.3/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "10.254.0.3/32",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.4/32": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.254.0.4/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.5/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.254.0.5/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r3/r3_route6.json b/tests/topotests/isis_topo1/r3/r3_route6.json
new file mode 100644
index 0000000..4104024
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_route6.json
@@ -0,0 +1,132 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::1/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth0"
+ }
+ ],
+ "prefix": "2001:db8:f::1/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::3/128": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "2001:db8:f::3/128",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::4/128": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:f::4/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::5/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:f::5/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r3/r3_route6_linux.json b/tests/topotests/isis_topo1/r3/r3_route6_linux.json
new file mode 100644
index 0000000..78993ff
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_route6_linux.json
@@ -0,0 +1,32 @@
+{
+ "2001:db8:1:2::/64": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::1": {
+ "dev": "r3-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::4": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::5": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r3/r3_route_linux.json b/tests/topotests/isis_topo1/r3/r3_route_linux.json
new file mode 100644
index 0000000..04a2418
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_route_linux.json
@@ -0,0 +1,32 @@
+{
+ "10.0.11.0/24": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ },
+ "10.0.21.0/24": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ },
+ "10.254.0.1": {
+ "dev": "r3-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.20.2"
+ },
+ "10.254.0.4": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ },
+ "10.254.0.5": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r3/r3_topology.json b/tests/topotests/isis_topo1/r3/r3_topology.json
new file mode 100644
index 0000000..2d36f9b
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/r3_topology.json
@@ -0,0 +1,194 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP internal",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.5/32"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.4/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "metric": "10",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::5/128"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "metric": "20",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "metric": "20",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::4/128"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP internal",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r1"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r1(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r1(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.1/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r1"
+ },
+ {
+ "metric": "10",
+ "interface": "r3-eth0",
+ "next-hop": "r1",
+ "parent": "r1(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::1/128"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1/r3/zebra.conf b/tests/topotests/isis_topo1/r3/zebra.conf
new file mode 100644
index 0000000..1e4c0d7
--- /dev/null
+++ b/tests/topotests/isis_topo1/r3/zebra.conf
@@ -0,0 +1,13 @@
+hostname r3
+interface r3-eth0
+ ip address 10.0.20.1/24
+ ipv6 address 2001:db8:1:1::1/64
+!
+interface r3-eth1
+ ip address 10.0.10.2/24
+ ipv6 address 2001:db8:2:1::2/64
+!
+interface lo
+ ip address 10.254.0.3/32
+ ipv6 address 2001:db8:F::3/128
+!
diff --git a/tests/topotests/isis_topo1/r4/isisd.conf b/tests/topotests/isis_topo1/r4/isisd.conf
new file mode 100644
index 0000000..50c876d
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/isisd.conf
@@ -0,0 +1,24 @@
+hostname r4
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r4-eth0
+ ip router isis 1
+ isis hello-interval 2
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+interface r4-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-1
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1/r4/r4_route.json b/tests/topotests/isis_topo1/r4/r4_route.json
new file mode 100644
index 0000000..79361af
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_route.json
@@ -0,0 +1,65 @@
+{
+ "10.0.11.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth0"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.254.0.2/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r4-eth0",
+ "ip": "10.0.21.2"
+ }
+ ],
+ "prefix": "10.254.0.2/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.4/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "10.254.0.4/32",
+ "protocol": "connected",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r4/r4_route6.json b/tests/topotests/isis_topo1/r4/r4_route6.json
new file mode 100644
index 0000000..c0ace9a
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_route6.json
@@ -0,0 +1,131 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::2/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth0"
+ }
+ ],
+ "prefix": "2001:db8:f::2/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::3/128": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:f::3/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::4/128": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "2001:db8:f::4/128",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::5/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:f::5/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ]}
diff --git a/tests/topotests/isis_topo1/r4/r4_route6_linux.json b/tests/topotests/isis_topo1/r4/r4_route6_linux.json
new file mode 100644
index 0000000..32ea366
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_route6_linux.json
@@ -0,0 +1,32 @@
+{
+ "2001:db8:1:1::/64": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:2:1::/64": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::2": {
+ "dev": "r4-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::3": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::5": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r4/r4_route_linux.json b/tests/topotests/isis_topo1/r4/r4_route_linux.json
new file mode 100644
index 0000000..5d6553f
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_route_linux.json
@@ -0,0 +1,32 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ },
+ "10.0.20.0/24": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ },
+ "10.254.0.2": {
+ "dev": "r4-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.21.2"
+ },
+ "10.254.0.3": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ },
+ "10.254.0.5": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r4/r4_topology.json b/tests/topotests/isis_topo1/r4/r4_topology.json
new file mode 100644
index 0000000..e7d7841
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/r4_topology.json
@@ -0,0 +1,194 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP internal",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.5/32"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.3/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "metric": "10",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::5/128"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "metric": "20",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "metric": "20",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::3/128"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP internal",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r2"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r2(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r2(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.2/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r2"
+ },
+ {
+ "metric": "10",
+ "interface": "r4-eth0",
+ "next-hop": "r2",
+ "parent": "r2(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::2/128"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1/r4/zebra.conf b/tests/topotests/isis_topo1/r4/zebra.conf
new file mode 100644
index 0000000..5ca9a3d
--- /dev/null
+++ b/tests/topotests/isis_topo1/r4/zebra.conf
@@ -0,0 +1,13 @@
+hostname r4
+interface r4-eth0
+ ip address 10.0.21.1/24
+ ipv6 address 2001:db8:1:2::1/64
+!
+interface r4-eth1
+ ip address 10.0.11.2/24
+ ipv6 address 2001:db8:2:2::2/64
+!
+interface lo
+ ip address 10.254.0.4/32
+ ipv6 address 2001:db8:F::4/128
+!
diff --git a/tests/topotests/isis_topo1/r5/isisd.conf b/tests/topotests/isis_topo1/r5/isisd.conf
new file mode 100644
index 0000000..e0e9200
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/isisd.conf
@@ -0,0 +1,23 @@
+hostname r5
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r5-eth0
+ ip router isis 1
+ isis hello-interval 2
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r5-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00
+ metric-style wide
+ is-type level-1
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
diff --git a/tests/topotests/isis_topo1/r5/r5_route.json b/tests/topotests/isis_topo1/r5/r5_route.json
new file mode 100644
index 0000000..cca844b
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_route.json
@@ -0,0 +1,145 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r5-eth0",
+ "ip": "10.0.10.2"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.0.11.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r5-eth1",
+ "ip": "10.0.11.2"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth0",
+ "ip": "10.0.10.2"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth1",
+ "ip": "10.0.11.2"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.3/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth0",
+ "ip": "10.0.10.2"
+ }
+ ],
+ "prefix": "10.254.0.3/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.4/32": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth1",
+ "ip": "10.0.11.2"
+ }
+ ],
+ "prefix": "10.254.0.4/32",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "10.254.0.5/32": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "10.254.0.5/32",
+ "protocol": "connected",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r5/r5_route6.json b/tests/topotests/isis_topo1/r5/r5_route6.json
new file mode 100644
index 0000000..b946876
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_route6.json
@@ -0,0 +1,115 @@
+{
+ "2001:db8:2:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "connected",
+ "selected": true
+ }
+ ],
+ "2001:db8:1:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::3/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "2001:db8:f::3/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::4/128": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "2001:db8:f::4/128",
+ "protocol": "isis",
+ "selected": true
+ }
+ ],
+ "2001:db8:f::5/128": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "lo"
+ }
+ ],
+ "prefix": "2001:db8:f::5/128",
+ "protocol": "connected",
+ "selected": true
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1/r5/r5_route6_linux.json b/tests/topotests/isis_topo1/r5/r5_route6_linux.json
new file mode 100644
index 0000000..a7343b5
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_route6_linux.json
@@ -0,0 +1,26 @@
+{
+ "2001:db8:2:1::/64": {
+ "dev": "r5-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r5-eth1",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:f::3": {
+ "dev": "r5-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:f::4": {
+ "dev": "r5-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r5/r5_route_linux.json b/tests/topotests/isis_topo1/r5/r5_route_linux.json
new file mode 100644
index 0000000..b809896
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_route_linux.json
@@ -0,0 +1,26 @@
+{
+ "10.0.20.0/24": {
+ "dev": "r5-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.2"
+ },
+ "10.0.21.0/24": {
+ "dev": "r5-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.2"
+ },
+ "10.254.0.3": {
+ "dev": "r5-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.2"
+ },
+ "10.254.0.4": {
+ "dev": "r5-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.2"
+ }
+}
diff --git a/tests/topotests/isis_topo1/r5/r5_topology.json b/tests/topotests/isis_topo1/r5/r5_topology.json
new file mode 100644
index 0000000..3d887b7
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/r5_topology.json
@@ -0,0 +1,156 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r5"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP internal",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP internal",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.3/32"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.254.0.4/32"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r5"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::3/128"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth1",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth1",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:f::4/128"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [],
+ "ipv6": []
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1/r5/zebra.conf b/tests/topotests/isis_topo1/r5/zebra.conf
new file mode 100644
index 0000000..48fed69
--- /dev/null
+++ b/tests/topotests/isis_topo1/r5/zebra.conf
@@ -0,0 +1,13 @@
+hostname r5
+interface r5-eth0
+ ip address 10.0.10.1/24
+ ipv6 address 2001:db8:2:1::1/64
+!
+interface r5-eth1
+ ip address 10.0.11.1/24
+ ipv6 address 2001:db8:2:2::1/64
+!
+interface lo
+ ip address 10.254.0.5/32
+ ipv6 address 2001:db8:F::5/128
+!
diff --git a/tests/topotests/isis_topo1/test_isis_topo1.dot b/tests/topotests/isis_topo1/test_isis_topo1.dot
new file mode 100644
index 0000000..01f9ba7
--- /dev/null
+++ b/tests/topotests/isis_topo1/test_isis_topo1.dot
@@ -0,0 +1,100 @@
+## 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="isis topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1\n10.254.0.1\n2001:DB8:F::1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2\n10.254.0.2\n2001:DB8:F::2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3\n10.254.0.3\n2001:DB8:F::3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon
+ label="r4\n10.254.0.4\n2001:DB8:F::4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5\n10.254.0.5\n2001:DB8:F::5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n10.0.20.0/24\n2001:DB8:1:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.21.0/24\n2001:DB8:1:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n10.0.10.0/24\n2001:DB8:2:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw4 [
+ shape=oval,
+ label="sw4\n10.0.11.0/24\n2001:DB8:2:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="level 2";
+
+ r1 -- sw1 [label="eth0\n.2"];
+ r2 -- sw2 [label="eth0\n.2"];
+ }
+
+ subgraph cluster1 {
+ label="level 1/2";
+
+ r3 -- sw1 [label="eth0\n.1"];
+ r3 -- sw3 [label="eth1\n.2"];
+
+ r4 -- sw4 [label="eth1\n.2"];
+ r4 -- sw2 [label="eth0\n.1"];
+ }
+
+ subgraph cluster2 {
+ label="level 1";
+
+ r5 -- sw3 [label="eth0\n.1"];
+ r5 -- sw4 [label="eth1\n.1"];
+ }
+}
diff --git a/tests/topotests/isis_topo1/test_isis_topo1.jpg b/tests/topotests/isis_topo1/test_isis_topo1.jpg
new file mode 100644
index 0000000..4ad730f
--- /dev/null
+++ b/tests/topotests/isis_topo1/test_isis_topo1.jpg
Binary files differ
diff --git a/tests/topotests/isis_topo1/test_isis_topo1.py b/tests/topotests/isis_topo1/test_isis_topo1.py
new file mode 100644
index 0000000..0147223
--- /dev/null
+++ b/tests/topotests/isis_topo1/test_isis_topo1.py
@@ -0,0 +1,497 @@
+#!/usr/bin/env python
+
+#
+# test_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_isis_topo1.py: Test ISIS topology.
+"""
+
+import functools
+import json
+import os
+import re
+import sys
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.isisd]
+
+VERTEX_TYPE_LIST = [
+ "pseudo_IS",
+ "pseudo_TE-IS",
+ "IS",
+ "TE-IS",
+ "ES",
+ "IP internal",
+ "IP external",
+ "IP TE",
+ "IP6 internal",
+ "IP6 external",
+ "UNKNOWN",
+]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Add ISIS routers:
+ # r1 r2
+ # | sw1 | sw2
+ # r3 r4
+ # | |
+ # sw3 sw4
+ # \ /
+ # r5
+ for routern in range(1, 6):
+ tgen.add_router("r{}".format(routern))
+
+ # r1 <- sw1 -> r3
+ sw = tgen.add_switch("sw1")
+ sw.add_link(tgen.gears["r1"])
+ sw.add_link(tgen.gears["r3"])
+
+ # r2 <- sw2 -> r4
+ sw = tgen.add_switch("sw2")
+ sw.add_link(tgen.gears["r2"])
+ sw.add_link(tgen.gears["r4"])
+
+ # r3 <- sw3 -> r5
+ sw = tgen.add_switch("sw3")
+ sw.add_link(tgen.gears["r3"])
+ sw.add_link(tgen.gears["r5"])
+
+ # r4 <- sw4 -> r5
+ sw = tgen.add_switch("sw4")
+ sw.add_link(tgen.gears["r4"])
+ sw.add_link(tgen.gears["r5"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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_isis_convergence():
+ "Wait for the protocol to converge before starting to test"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for ISIS protocol to converge")
+ # Code to generate the json files.
+ # for rname, router in tgen.routers().items():
+ # open('/tmp/{}_topology.json'.format(rname), 'w').write(
+ # json.dumps(show_isis_topology(router), indent=2, sort_keys=True)
+ # )
+
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_topology.json".format(CWD, rname)
+ expected = json.loads(open(filename).read())
+
+ def compare_isis_topology(router, expected):
+ "Helper function to test ISIS topology convergence."
+ actual = show_isis_topology(router)
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(compare_isis_topology, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120)
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_isis_route_installation():
+ "Check whether all expected routes are present"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS routes")
+
+ # Check for routes in 'show ip route json'
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+
+ def compare_isis_installed_routes(router, expected):
+ "Helper function to test ISIS routes installed in rib."
+ actual = router.vtysh_cmd("show ip route json", isjson=True)
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(compare_isis_installed_routes, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=1, count=10)
+ assertmsg = "Router '{}' routes mismatch".format(rname)
+ assert result, assertmsg
+
+
+def test_isis_linux_route_installation():
+ "Check whether all expected routes are present and installed in the OS"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS routes in OS")
+
+ # Check for routes in `ip route`
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+ actual = topotest.ip4_route(router)
+ assertmsg = "Router '{}' OS routes mismatch".format(rname)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+def test_isis_route6_installation():
+ "Check whether all expected routes are present"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS IPv6 routes")
+
+ # Check for routes in 'show ip route json'
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route6.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+
+ def compare_isis_v6_installed_routes(router, expected):
+ "Helper function to test ISIS v6 routes installed in rib."
+ actual = router.vtysh_cmd("show ipv6 route json", isjson=True)
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(
+ compare_isis_v6_installed_routes, router, expected
+ )
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=1, count=10)
+ assertmsg = "Router '{}' routes mismatch".format(rname)
+ assert result, assertmsg
+
+
+def test_isis_linux_route6_installation():
+ "Check whether all expected routes are present and installed in the OS"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS IPv6 routes in OS")
+
+ # Check for routes in `ip route`
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+ actual = topotest.ip6_route(router)
+ assertmsg = "Router '{}' OS routes mismatch".format(rname)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+def test_isis_summary_json():
+ "Check json struct in show isis summary json"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking 'show isis summary json'")
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis summary json", isjson=True)
+ assertmsg = "Test isis summary json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['vrf'] == "default", assertmsg
+ assert json_output['areas'][0]['area'] == "1", assertmsg
+ assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+
+
+def test_isis_interface_json():
+ "Check json struct in show isis interface json"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking 'show isis interface json'")
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis interface json", isjson=True)
+ assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis interface detail json", isjson=True)
+ assertmsg = "Test isis interface json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+
+
+def test_isis_neighbor_json():
+ "Check json struct in show isis neighbor json"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #tgen.mininet_cli()
+ logger.info("Checking 'show isis neighbor json'")
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor json", isjson=True)
+ assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['circuits'][0]['interface'] == rname+"-eth0", assertmsg
+
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis neighbor detail json", isjson=True)
+ assertmsg = "Test isis neighbor json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['circuits'][0]['interface']['name'] == rname+"-eth0", assertmsg
+
+
+def test_isis_database_json():
+ "Check json struct in show isis database json"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #tgen.mininet_cli()
+ logger.info("Checking 'show isis database json'")
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis database json", isjson=True)
+ assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['area']['name'] == "1", assertmsg
+ assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+
+ for rname, router in tgen.routers().items():
+ logger.info("Checking router %s", rname)
+ json_output = tgen.gears[rname].vtysh_cmd("show isis database detail json", isjson=True)
+ assertmsg = "Test isis database json failed in '{}' data '{}'".format(rname, json_output)
+ assert json_output['areas'][0]['area']['name'] == "1", assertmsg
+ assert json_output['areas'][0]['levels'][0]['id'] != '3', assertmsg
+
+
+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))
+
+
+#
+# Auxiliary functions
+#
+
+
+def dict_merge(dct, merge_dct):
+ """
+ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
+ updating only top-level keys, dict_merge recurses down into dicts nested
+ to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
+ ``dct``.
+ :param dct: dict onto which the merge is executed
+ :param merge_dct: dct merged into dct
+ :return: None
+
+ Source:
+ https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
+ """
+ for k, v in merge_dct.items():
+ if k in dct and isinstance(dct[k], dict) and topotest.is_mapping(merge_dct[k]):
+ dict_merge(dct[k], merge_dct[k])
+ else:
+ dct[k] = merge_dct[k]
+
+
+def parse_topology(lines, level):
+ """
+ Parse the output of 'show isis topology level-X' into a Python dict.
+ """
+ areas = {}
+ area = None
+ ipv = None
+ vertex_type_regex = "|".join(VERTEX_TYPE_LIST)
+
+ for line in lines:
+ area_match = re.match(r"Area (.+):", line)
+ if area_match:
+ area = area_match.group(1)
+ if area not in areas:
+ areas[area] = {level: {"ipv4": [], "ipv6": []}}
+ ipv = None
+ continue
+ elif area is None:
+ continue
+
+ if re.match(r"IS\-IS paths to level-. routers that speak IPv6", line):
+ ipv = "ipv6"
+ continue
+ if re.match(r"IS\-IS paths to level-. routers that speak IP", line):
+ ipv = "ipv4"
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)", line
+ )
+ if (
+ item_match is not None
+ and item_match.group(1) == "Vertex"
+ and item_match.group(2) == "Type"
+ and item_match.group(3) == "Metric"
+ and item_match.group(4) == "Next-Hop"
+ and item_match.group(5) == "Interface"
+ and item_match.group(6) == "Parent"
+ ):
+ # Skip header
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+) ([^\s]+) ([^\s]+)".format(
+ vertex_type_regex
+ ),
+ line,
+ )
+ if item_match is not None:
+ areas[area][level][ipv].append(
+ {
+ "vertex": item_match.group(1),
+ "type": item_match.group(2),
+ "metric": item_match.group(3),
+ "next-hop": item_match.group(5),
+ "interface": item_match.group(6),
+ "parent": item_match.group(7),
+ }
+ )
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+)".format(vertex_type_regex),
+ line,
+ )
+
+ if item_match is not None:
+ areas[area][level][ipv].append(
+ {
+ "vertex": item_match.group(1),
+ "type": item_match.group(2),
+ "metric": item_match.group(3),
+ "parent": item_match.group(5),
+ }
+ )
+ continue
+
+ item_match = re.match(r"([^\s]+)", line)
+ if item_match is not None:
+ areas[area][level][ipv].append({"vertex": item_match.group(1)})
+ continue
+
+ return areas
+
+
+def show_isis_topology(router):
+ """
+ Get the ISIS topology in a dictionary format.
+
+ Sample:
+ {
+ 'area-name': {
+ 'level-1': [
+ {
+ 'vertex': 'r1'
+ }
+ ],
+ 'level-2': [
+ {
+ 'vertex': '10.0.0.1/24',
+ 'type': 'IP',
+ 'parent': '0',
+ 'metric': 'internal'
+ }
+ ]
+ },
+ 'area-name-2': {
+ 'level-2': [
+ {
+ "interface": "rX-ethY",
+ "metric": "Z",
+ "next-hop": "rA",
+ "parent": "rC(B)",
+ "type": "TE-IS",
+ "vertex": "rD"
+ }
+ ]
+ }
+ }
+ """
+ l1out = topotest.normalize_text(
+ router.vtysh_cmd("show isis topology level-1")
+ ).splitlines()
+ l2out = topotest.normalize_text(
+ router.vtysh_cmd("show isis topology level-2")
+ ).splitlines()
+
+ l1 = parse_topology(l1out, "level-1")
+ l2 = parse_topology(l2out, "level-2")
+
+ dict_merge(l1, l2)
+ return l1
diff --git a/tests/topotests/isis_topo1_vrf/__init__.py b/tests/topotests/isis_topo1_vrf/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/__init__.py
diff --git a/tests/topotests/isis_topo1_vrf/r1/isisd.conf b/tests/topotests/isis_topo1_vrf/r1/isisd.conf
new file mode 100755
index 0000000..4dd6815
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/isisd.conf
@@ -0,0 +1,16 @@
+hostname r1
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r1-eth0 vrf r1-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+router isis 1 vrf r1-cust1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0000.00
+ metric-style wide
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1_vrf/r1/r1_route.json b/tests/topotests/isis_topo1_vrf/r1/r1_route.json
new file mode 100644
index 0000000..8359baa
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/r1_route.json
@@ -0,0 +1,37 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r1-eth0",
+ "ip": "10.0.20.1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r1-cust1"
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r1-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r1/r1_route6.json b/tests/topotests/isis_topo1_vrf/r1/r1_route6.json
new file mode 100644
index 0000000..7496126
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/r1_route6.json
@@ -0,0 +1,36 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r1-cust1"
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r1-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r1-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r1/r1_route6_linux.json b/tests/topotests/isis_topo1_vrf/r1/r1_route6_linux.json
new file mode 100755
index 0000000..d1ace40
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/r1_route6_linux.json
@@ -0,0 +1,14 @@
+{
+ "2001:db8:1:1::/64": {
+ "dev": "r1-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:1::/64": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r1/r1_route_linux.json b/tests/topotests/isis_topo1_vrf/r1/r1_route_linux.json
new file mode 100755
index 0000000..6af2229
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/r1_route_linux.json
@@ -0,0 +1,13 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r1-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.20.1"
+ },
+ "10.0.20.0/24": {
+ "dev": "r1-eth0",
+ "proto": "kernel",
+ "scope": "link"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r1/r1_topology.json b/tests/topotests/isis_topo1_vrf/r1/r1_topology.json
new file mode 100644
index 0000000..666fa52
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/r1_topology.json
@@ -0,0 +1,80 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r1"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r1"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r1"
+ },
+ {
+ "metric": "0",
+ "parent": "r1(4)",
+ "type": "IP internal",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r1(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r1"
+ },
+ {
+ "metric": "0",
+ "parent": "r1(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "interface": "r1-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r1(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "metric": "10",
+ "interface": "r1-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r1/zebra.conf b/tests/topotests/isis_topo1_vrf/r1/zebra.conf
new file mode 100755
index 0000000..fa1c02e
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r1/zebra.conf
@@ -0,0 +1,9 @@
+hostname r1
+interface r1-eth0 vrf r1-cust1
+ ip address 10.0.20.2/24
+ ipv6 address 2001:db8:1:1::2/64
+!
+interface lo
+ ip address 10.254.0.1/32
+ ipv6 address 2001:db8:F::1/128
+!
diff --git a/tests/topotests/isis_topo1_vrf/r2/isisd.conf b/tests/topotests/isis_topo1_vrf/r2/isisd.conf
new file mode 100755
index 0000000..955bb54
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/isisd.conf
@@ -0,0 +1,16 @@
+hostname r2
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r2-eth0 vrf r2-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+router isis 1 vrf r2-cust1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1_vrf/r2/r2_route.json b/tests/topotests/isis_topo1_vrf/r2/r2_route.json
new file mode 100644
index 0000000..e2eee11
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/r2_route.json
@@ -0,0 +1,37 @@
+{
+ "10.0.11.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r2-eth0",
+ "ip": "10.0.21.1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r2-cust1"
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r2-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r2/r2_route6.json b/tests/topotests/isis_topo1_vrf/r2/r2_route6.json
new file mode 100644
index 0000000..21b953d
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/r2_route6.json
@@ -0,0 +1,36 @@
+{
+ "2001:db8:1:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r2-cust1"
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r2-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r2-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r2/r2_route6_linux.json b/tests/topotests/isis_topo1_vrf/r2/r2_route6_linux.json
new file mode 100755
index 0000000..27423e1
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/r2_route6_linux.json
@@ -0,0 +1,14 @@
+{
+ "2001:db8:1:2::/64": {
+ "dev": "r2-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r2/r2_route_linux.json b/tests/topotests/isis_topo1_vrf/r2/r2_route_linux.json
new file mode 100755
index 0000000..744b078
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/r2_route_linux.json
@@ -0,0 +1,13 @@
+{
+ "10.0.11.0/24": {
+ "dev": "r2-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.21.1"
+ },
+ "10.0.21.0/24": {
+ "dev": "r2-eth0",
+ "proto": "kernel",
+ "scope": "link"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r2/r2_topology.json b/tests/topotests/isis_topo1_vrf/r2/r2_topology.json
new file mode 100644
index 0000000..c26ad1e
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/r2_topology.json
@@ -0,0 +1,80 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r2"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r2"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r2"
+ },
+ {
+ "metric": "0",
+ "parent": "r2(4)",
+ "type": "IP internal",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r2(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r2"
+ },
+ {
+ "metric": "0",
+ "parent": "r2(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "interface": "r2-eth0",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r2(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "metric": "10",
+ "interface": "r2-eth0",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r2/zebra.conf b/tests/topotests/isis_topo1_vrf/r2/zebra.conf
new file mode 100755
index 0000000..a62af17
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r2/zebra.conf
@@ -0,0 +1,9 @@
+hostname r2
+interface r2-eth0 vrf r2-cust1
+ ip address 10.0.21.2/24
+ ipv6 address 2001:db8:1:2::2/64
+!
+interface lo
+ ip address 10.254.0.2/32
+ ipv6 address 2001:db8:F::2/128
+!
diff --git a/tests/topotests/isis_topo1_vrf/r3/isisd.conf b/tests/topotests/isis_topo1_vrf/r3/isisd.conf
new file mode 100755
index 0000000..d127b3a
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/isisd.conf
@@ -0,0 +1,23 @@
+hostname r3
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r3-eth0 vrf r3-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+interface r3-eth1 vrf r3-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1 vrf r3-cust1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-1
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1_vrf/r3/r3_route.json b/tests/topotests/isis_topo1_vrf/r3/r3_route.json
new file mode 100644
index 0000000..33ad90c
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/r3_route.json
@@ -0,0 +1,86 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis",
+ "vrfName": "r3-cust1"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "10.0.11.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth0"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r3-eth1",
+ "ip": "10.0.10.1"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r3/r3_route6.json b/tests/topotests/isis_topo1_vrf/r3/r3_route6.json
new file mode 100644
index 0000000..519fe49
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/r3_route6.json
@@ -0,0 +1,70 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r3-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r3-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r3/r3_route6_linux.json b/tests/topotests/isis_topo1_vrf/r3/r3_route6_linux.json
new file mode 100755
index 0000000..bc527d2
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/r3_route6_linux.json
@@ -0,0 +1,26 @@
+{
+ "2001:db8:1:1::/64": {
+ "dev": "r3-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:1:2::/64": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:2:1::/64": {
+ "dev": "r3-eth1",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r3/r3_route_linux.json b/tests/topotests/isis_topo1_vrf/r3/r3_route_linux.json
new file mode 100755
index 0000000..515d376
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/r3_route_linux.json
@@ -0,0 +1,24 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r3-eth1",
+ "proto": "kernel",
+ "scope": "link"
+ },
+ "10.0.11.0/24": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ },
+ "10.0.20.0/24": {
+ "dev": "r3-eth0",
+ "proto": "kernel",
+ "scope": "link"
+ },
+ "10.0.21.0/24": {
+ "dev": "r3-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.1"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r3/r3_topology.json b/tests/topotests/isis_topo1_vrf/r3/r3_topology.json
new file mode 100644
index 0000000..044a6c0
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/r3_topology.json
@@ -0,0 +1,132 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP internal",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "interface": "r3-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "metric": "10",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "metric": "20",
+ "interface": "r3-eth1",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP internal",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r1"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r1(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r3"
+ },
+ {
+ "metric": "0",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "interface": "r3-eth0",
+ "metric": "10",
+ "next-hop": "r1",
+ "parent": "r3(4)",
+ "type": "TE-IS",
+ "vertex": "r1"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r3/zebra.conf b/tests/topotests/isis_topo1_vrf/r3/zebra.conf
new file mode 100755
index 0000000..ac0b810
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r3/zebra.conf
@@ -0,0 +1,13 @@
+hostname r3
+interface r3-eth0 vrf r3-cust1
+ ip address 10.0.20.1/24
+ ipv6 address 2001:db8:1:1::1/64
+!
+interface r3-eth1 vrf r3-cust1
+ ip address 10.0.10.2/24
+ ipv6 address 2001:db8:2:1::2/64
+!
+interface lo
+ ip address 10.254.0.3/32
+ ipv6 address 2001:db8:F::3/128
+!
diff --git a/tests/topotests/isis_topo1_vrf/r4/isisd.conf b/tests/topotests/isis_topo1_vrf/r4/isisd.conf
new file mode 100755
index 0000000..fd11b2c
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/isisd.conf
@@ -0,0 +1,26 @@
+hostname r4
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis lsp-gen
+! debug isis lsp-sched
+
+interface r4-eth0 vrf r4-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-2-only
+!
+interface r4-eth1 vrf r4-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1 vrf r4-cust1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0004.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv4 connected level-2
+ redistribute ipv6 connected level-1
+ redistribute ipv6 connected level-2
+!
diff --git a/tests/topotests/isis_topo1_vrf/r4/r4_route.json b/tests/topotests/isis_topo1_vrf/r4/r4_route.json
new file mode 100644
index 0000000..6fb3bd9
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/r4_route.json
@@ -0,0 +1,81 @@
+{
+ "10.0.10.0/24": [
+ {
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r4-eth1",
+ "ip": "10.0.11.1"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "10.0.11.0/24": [
+ {
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r4-eth1",
+ "ip": "10.0.11.1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "vrfName": "r4-cust1"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r4-eth1",
+ "ip": "10.0.11.1"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth0"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r4/r4_route6.json b/tests/topotests/isis_topo1_vrf/r4/r4_route6.json
new file mode 100644
index 0000000..702a83f
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/r4_route6.json
@@ -0,0 +1,70 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "distance": 115,
+ "metric": 20,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r4-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r4-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r4/r4_route6_linux.json b/tests/topotests/isis_topo1_vrf/r4/r4_route6_linux.json
new file mode 100755
index 0000000..b1cd5b9
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/r4_route6_linux.json
@@ -0,0 +1,26 @@
+{
+ "2001:db8:1:1::/64": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:1:2::/64": {
+ "dev": "r4-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:1::/64": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r4-eth1",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r4/r4_route_linux.json b/tests/topotests/isis_topo1_vrf/r4/r4_route_linux.json
new file mode 100755
index 0000000..3198b85
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/r4_route_linux.json
@@ -0,0 +1,24 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ },
+ "10.0.11.0/24": {
+ "dev": "r4-eth1",
+ "proto": "kernel",
+ "scope": "link"
+ },
+ "10.0.20.0/24": {
+ "dev": "r4-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.1"
+ },
+ "10.0.21.0/24": {
+ "dev": "r4-eth0",
+ "proto": "kernel",
+ "scope": "link"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r4/r4_topology.json b/tests/topotests/isis_topo1_vrf/r4/r4_topology.json
new file mode 100644
index 0000000..d40008a
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/r4_topology.json
@@ -0,0 +1,132 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP internal",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "20",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "interface": "r4-eth1",
+ "metric": "10",
+ "next-hop": "r5",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r5"
+ },
+ {
+ "metric": "10",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "metric": "20",
+ "interface": "r4-eth1",
+ "next-hop": "r5",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP internal",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r2"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r2(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r4"
+ },
+ {
+ "metric": "0",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ },
+ {
+ "interface": "r4-eth0",
+ "metric": "10",
+ "next-hop": "r2",
+ "parent": "r4(4)",
+ "type": "TE-IS",
+ "vertex": "r2"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r4/zebra.conf b/tests/topotests/isis_topo1_vrf/r4/zebra.conf
new file mode 100755
index 0000000..9c8941f
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r4/zebra.conf
@@ -0,0 +1,13 @@
+hostname r4
+interface r4-eth0 vrf r4-cust1
+ ip address 10.0.21.1/24
+ ipv6 address 2001:db8:1:2::1/64
+!
+interface r4-eth1 vrf r4-cust1
+ ip address 10.0.11.2/24
+ ipv6 address 2001:db8:2:2::2/64
+!
+interface lo
+ ip address 10.254.0.4/32
+ ipv6 address 2001:db8:F::4/128
+!
diff --git a/tests/topotests/isis_topo1_vrf/r5/isisd.conf b/tests/topotests/isis_topo1_vrf/r5/isisd.conf
new file mode 100755
index 0000000..6777637
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/isisd.conf
@@ -0,0 +1,22 @@
+hostname r5
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+interface r5-eth0 vrf r5-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+interface r5-eth1 vrf r5-cust1
+ ip router isis 1
+ ipv6 router isis 1
+ isis circuit-type level-1
+!
+router isis 1 vrf r5-cust1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0005.00
+ metric-style wide
+ is-type level-1
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+!
diff --git a/tests/topotests/isis_topo1_vrf/r5/r5_route.json b/tests/topotests/isis_topo1_vrf/r5/r5_route.json
new file mode 100644
index 0000000..a254b6f
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/r5_route.json
@@ -0,0 +1,94 @@
+{
+ "10.0.10.0/24": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r5-eth0",
+ "ip": "10.0.10.2"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "isis",
+ "vrfName": "r5-cust1"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "10.0.10.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "10.0.11.0/24": [
+ {
+ "nexthops": [
+ {
+ "afi": "ipv4",
+ "interfaceName": "r5-eth1",
+ "ip": "10.0.11.2"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "isis",
+ "vrfName": "r5-cust1"
+ },
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "10.0.11.0/24",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "10.0.20.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth0",
+ "ip": "10.0.10.2"
+ }
+ ],
+ "prefix": "10.0.20.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "10.0.21.0/24": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv4",
+ "fib": true,
+ "interfaceName": "r5-eth1",
+ "ip": "10.0.11.2"
+ }
+ ],
+ "prefix": "10.0.21.0/24",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r5/r5_route6.json b/tests/topotests/isis_topo1_vrf/r5/r5_route6.json
new file mode 100644
index 0000000..06fc78f
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/r5_route6.json
@@ -0,0 +1,70 @@
+{
+ "2001:db8:1:1::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "2001:db8:1:1::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "2001:db8:1:2::/64": [
+ {
+ "distance": 115,
+ "metric": 10,
+ "nexthops": [
+ {
+ "active": true,
+ "afi": "ipv6",
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "2001:db8:1:2::/64",
+ "protocol": "isis",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "2001:db8:2:1::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth0"
+ }
+ ],
+ "prefix": "2001:db8:2:1::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ],
+ "2001:db8:2:2::/64": [
+ {
+ "nexthops": [
+ {
+ "active": true,
+ "directlyConnected": true,
+ "fib": true,
+ "interfaceName": "r5-eth1"
+ }
+ ],
+ "prefix": "2001:db8:2:2::/64",
+ "protocol": "connected",
+ "selected": true,
+ "vrfName": "r5-cust1"
+ }
+ ]
+}
diff --git a/tests/topotests/isis_topo1_vrf/r5/r5_route6_linux.json b/tests/topotests/isis_topo1_vrf/r5/r5_route6_linux.json
new file mode 100755
index 0000000..3db3c93
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/r5_route6_linux.json
@@ -0,0 +1,26 @@
+{
+ "2001:db8:1:1::/64": {
+ "dev": "r5-eth0",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:1:2::/64": {
+ "dev": "r5-eth1",
+ "metric": "20",
+ "pref": "medium",
+ "proto": "187"
+ },
+ "2001:db8:2:1::/64": {
+ "dev": "r5-eth0",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ },
+ "2001:db8:2:2::/64": {
+ "dev": "r5-eth1",
+ "metric": "256",
+ "pref": "medium",
+ "proto": "kernel"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r5/r5_route_linux.json b/tests/topotests/isis_topo1_vrf/r5/r5_route_linux.json
new file mode 100755
index 0000000..6a38ba8
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/r5_route_linux.json
@@ -0,0 +1,24 @@
+{
+ "10.0.10.0/24": {
+ "dev": "r5-eth0",
+ "proto": "kernel",
+ "scope": "link"
+ },
+ "10.0.11.0/24": {
+ "dev": "r5-eth1",
+ "proto": "kernel",
+ "scope": "link"
+ },
+ "10.0.20.0/24": {
+ "dev": "r5-eth0",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.10.2"
+ },
+ "10.0.21.0/24": {
+ "dev": "r5-eth1",
+ "metric": "20",
+ "proto": "187",
+ "via": "10.0.11.2"
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r5/r5_topology.json b/tests/topotests/isis_topo1_vrf/r5/r5_topology.json
new file mode 100644
index 0000000..2a088ca
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/r5_topology.json
@@ -0,0 +1,124 @@
+{
+ "1": {
+ "level-1": {
+ "ipv4": [
+ {
+ "vertex": "r5"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP internal",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP internal",
+ "vertex": "10.0.11.0/24"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.20.0/24"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP TE",
+ "vertex": "10.0.10.0/24"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.21.0/24"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP TE",
+ "vertex": "10.0.11.0/24"
+ }
+ ],
+ "ipv6": [
+ {
+ "vertex": "r5"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:1::/64"
+ },
+ {
+ "metric": "0",
+ "parent": "r5(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:2:2::/64"
+ },
+ {
+ "interface": "r5-eth0",
+ "metric": "10",
+ "next-hop": "r3",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r3"
+ },
+ {
+ "interface": "r5-eth1",
+ "metric": "10",
+ "next-hop": "r4",
+ "parent": "r5(4)",
+ "type": "TE-IS",
+ "vertex": "r4"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth0",
+ "next-hop": "r3",
+ "parent": "r3(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:1::/64"
+ },
+ {
+ "metric": "10",
+ "interface": "r5-eth1",
+ "next-hop": "r4",
+ "parent": "r4(4)",
+ "type": "IP6 internal",
+ "vertex": "2001:db8:1:2::/64"
+ }
+ ]
+ },
+ "level-2": {
+ "ipv4": [],
+ "ipv6": []
+ }
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/r5/zebra.conf b/tests/topotests/isis_topo1_vrf/r5/zebra.conf
new file mode 100755
index 0000000..c6bc630
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/r5/zebra.conf
@@ -0,0 +1,13 @@
+hostname r5
+interface r5-eth0 vrf r5-cust1
+ ip address 10.0.10.1/24
+ ipv6 address 2001:db8:2:1::1/64
+!
+interface r5-eth1 vrf r5-cust1
+ ip address 10.0.11.1/24
+ ipv6 address 2001:db8:2:2::1/64
+!
+interface lo
+ ip address 10.254.0.5/32
+ ipv6 address 2001:db8:F::5/128
+!
diff --git a/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.dot b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.dot
new file mode 100755
index 0000000..01f9ba7
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.dot
@@ -0,0 +1,100 @@
+## 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="isis topo1";
+
+ # Routers
+ r1 [
+ shape=doubleoctagon,
+ label="r1\n10.254.0.1\n2001:DB8:F::1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ shape=doubleoctagon
+ label="r2\n10.254.0.2\n2001:DB8:F::2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ shape=doubleoctagon
+ label="r3\n10.254.0.3\n2001:DB8:F::3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ shape=doubleoctagon
+ label="r4\n10.254.0.4\n2001:DB8:F::4",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r5 [
+ shape=doubleoctagon
+ label="r5\n10.254.0.5\n2001:DB8:F::5",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ sw1 [
+ shape=oval,
+ label="sw1\n10.0.20.0/24\n2001:DB8:1:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.21.0/24\n2001:DB8:1:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n10.0.10.0/24\n2001:DB8:2:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw4 [
+ shape=oval,
+ label="sw4\n10.0.11.0/24\n2001:DB8:2:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="level 2";
+
+ r1 -- sw1 [label="eth0\n.2"];
+ r2 -- sw2 [label="eth0\n.2"];
+ }
+
+ subgraph cluster1 {
+ label="level 1/2";
+
+ r3 -- sw1 [label="eth0\n.1"];
+ r3 -- sw3 [label="eth1\n.2"];
+
+ r4 -- sw4 [label="eth1\n.2"];
+ r4 -- sw2 [label="eth0\n.1"];
+ }
+
+ subgraph cluster2 {
+ label="level 1";
+
+ r5 -- sw3 [label="eth0\n.1"];
+ r5 -- sw4 [label="eth1\n.1"];
+ }
+}
diff --git a/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.jpg b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.jpg
new file mode 100755
index 0000000..4ad730f
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.jpg
Binary files differ
diff --git a/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py
new file mode 100644
index 0000000..ff9ad61
--- /dev/null
+++ b/tests/topotests/isis_topo1_vrf/test_isis_topo1_vrf.py
@@ -0,0 +1,441 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2020 by Niral Networks, Inc. ("Niral Networks")
+# 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 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_isis_topo1_vrf.py: Test ISIS vrf topology.
+"""
+
+import functools
+import json
+import os
+import re
+import sys
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import required_linux_kernel_version
+
+
+pytestmark = [pytest.mark.isisd]
+
+VERTEX_TYPE_LIST = [
+ "pseudo_IS",
+ "pseudo_TE-IS",
+ "IS",
+ "TE-IS",
+ "ES",
+ "IP internal",
+ "IP external",
+ "IP TE",
+ "IP6 internal",
+ "IP6 external",
+ "UNKNOWN",
+]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Add ISIS routers:
+ # r1 r2
+ # | sw1 | sw2
+ # r3 r4
+ # | |
+ # sw3 sw4
+ # \ /
+ # r5
+ for routern in range(1, 6):
+ tgen.add_router("r{}".format(routern))
+
+ # r1 <- sw1 -> r3
+ sw = tgen.add_switch("sw1")
+ sw.add_link(tgen.gears["r1"])
+ sw.add_link(tgen.gears["r3"])
+
+ # r2 <- sw2 -> r4
+ sw = tgen.add_switch("sw2")
+ sw.add_link(tgen.gears["r2"])
+ sw.add_link(tgen.gears["r4"])
+
+ # r3 <- sw3 -> r5
+ sw = tgen.add_switch("sw3")
+ sw.add_link(tgen.gears["r3"])
+ sw.add_link(tgen.gears["r5"])
+
+ # r4 <- sw4 -> r5
+ sw = tgen.add_switch("sw4")
+ sw.add_link(tgen.gears["r4"])
+ sw.add_link(tgen.gears["r5"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ logger.info("Testing with VRF Lite support")
+
+ cmds = [
+ "ip link add {0}-cust1 type vrf table 1001",
+ "ip link add loop1 type dummy",
+ "ip link set {0}-eth0 master {0}-cust1",
+ ]
+
+ eth1_cmds = ["ip link set {0}-eth1 master {0}-cust1"]
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ # create VRF rx-cust1 and link rx-eth0 to rx-cust1
+ for cmd in cmds:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+
+ # If router has an rX-eth1, link that to vrf also
+ if "{}-eth1".format(rname) in router.links.keys():
+ for cmd in eth1_cmds:
+ output = output + tgen.net[rname].cmd(cmd.format(rname))
+
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ # After loading the configurations, this function loads configured daemons.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ # move back rx-eth0 to default VRF
+ # delete rx-vrf
+ tgen.stop_topology()
+
+
+def test_isis_convergence():
+ "Wait for the protocol to converge before starting to test"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for ISIS protocol to converge")
+
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_topology.json".format(CWD, rname)
+ expected = json.loads(open(filename).read())
+
+ def compare_isis_topology(router, expected):
+ "Helper function to test ISIS vrf topology convergence."
+ actual = show_isis_topology(router)
+
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(compare_isis_topology, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=120)
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_isis_route_installation():
+ "Check whether all expected routes are present"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS vrf routes")
+ # Check for routes in 'show ip route vrf {}-cust1 json'
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+
+ def compare_routing_table(router, expected):
+ "Helper function to ensure zebra rib convergence"
+
+ actual = router.vtysh_cmd(
+ "show ip route vrf {0}-cust1 json".format(rname), isjson=True
+ )
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(compare_routing_table, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = "Router '{}' routes mismatch diff: {}".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_isis_linux_route_installation():
+ "Check whether all expected routes are present and installed in the OS"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ logger.info("Checking routers for installed ISIS vrf routes in OS")
+ # Check for routes in `ip route show vrf {}-cust1`
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route_linux.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+ actual = topotest.ip4_vrf_route(router)
+ assertmsg = "Router '{}' OS routes mismatch".format(rname)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+def test_isis_route6_installation():
+ "Check whether all expected routes are present"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking routers for installed ISIS vrf IPv6 routes")
+ # Check for routes in 'show ipv6 route vrf {}-cust1 json'
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route6.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+
+ def compare_routing_table(router, expected):
+ "Helper function to ensure zebra rib convergence"
+ actual = router.vtysh_cmd(
+ "show ipv6 route vrf {}-cust1 json".format(rname), isjson=True
+ )
+ return topotest.json_cmp(actual, expected)
+
+ test_func = functools.partial(compare_routing_table, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = "Router '{}' routes mismatch diff: ".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_isis_linux_route6_installation():
+ "Check whether all expected routes are present and installed in the OS"
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ logger.info("Checking routers for installed ISIS vrf IPv6 routes in OS")
+ # Check for routes in `ip -6 route show vrf {}-cust1`
+ for rname, router in tgen.routers().items():
+ filename = "{0}/{1}/{1}_route6_linux.json".format(CWD, rname)
+ expected = json.loads(open(filename, "r").read())
+ actual = topotest.ip6_vrf_route(router)
+ assertmsg = "Router '{}' OS routes mismatch".format(rname)
+ assert topotest.json_cmp(actual, expected) is None, assertmsg
+
+
+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))
+
+
+#
+# Auxiliary functions
+#
+
+
+def dict_merge(dct, merge_dct):
+ """
+ Recursive dict merge. Inspired by :meth:``dict.update()``, instead of
+ updating only top-level keys, dict_merge recurses down into dicts nested
+ to an arbitrary depth, updating keys. The ``merge_dct`` is merged into
+ ``dct``.
+ :param dct: dict onto which the merge is executed
+ :param merge_dct: dct merged into dct
+ :return: None
+
+ Source:
+ https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
+ """
+ for k, v in merge_dct.items():
+ if k in dct and isinstance(dct[k], dict) and topotest.is_mapping(merge_dct[k]):
+ dict_merge(dct[k], merge_dct[k])
+ else:
+ dct[k] = merge_dct[k]
+
+
+def parse_topology(lines, level):
+ """
+ Parse the output of 'show isis topology level-X' into a Python dict.
+ """
+ areas = {}
+ area = None
+ ipv = None
+ vertex_type_regex = "|".join(VERTEX_TYPE_LIST)
+
+ for line in lines:
+ area_match = re.match(r"Area (.+):", line)
+ if area_match:
+ area = area_match.group(1)
+ if area not in areas:
+ areas[area] = {level: {"ipv4": [], "ipv6": []}}
+ ipv = None
+ continue
+ elif area is None:
+ continue
+
+ if re.match(r"IS\-IS paths to level-. routers that speak IPv6", line):
+ ipv = "ipv6"
+ continue
+ if re.match(r"IS\-IS paths to level-. routers that speak IP", line):
+ ipv = "ipv4"
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+) ([^\s]+)", line
+ )
+ if (
+ item_match is not None
+ and item_match.group(1) == "Vertex"
+ and item_match.group(2) == "Type"
+ and item_match.group(3) == "Metric"
+ and item_match.group(4) == "Next-Hop"
+ and item_match.group(5) == "Interface"
+ and item_match.group(6) == "Parent"
+ ):
+ # Skip header
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+) ([^\s]+) ([^\s]+)".format(
+ vertex_type_regex
+ ),
+ line,
+ )
+ if item_match is not None:
+ areas[area][level][ipv].append(
+ {
+ "vertex": item_match.group(1),
+ "type": item_match.group(2),
+ "metric": item_match.group(3),
+ "next-hop": item_match.group(5),
+ "interface": item_match.group(6),
+ "parent": item_match.group(7),
+ }
+ )
+ continue
+
+ item_match = re.match(
+ r"([^\s]+) ({}) ([0]|([1-9][0-9]*)) ([^\s]+)".format(vertex_type_regex),
+ line,
+ )
+
+ if item_match is not None:
+ areas[area][level][ipv].append(
+ {
+ "vertex": item_match.group(1),
+ "type": item_match.group(2),
+ "metric": item_match.group(3),
+ "parent": item_match.group(5),
+ }
+ )
+ continue
+
+ item_match = re.match(r"([^\s]+)", line)
+ if item_match is not None:
+ areas[area][level][ipv].append({"vertex": item_match.group(1)})
+ continue
+
+ return areas
+
+
+def show_isis_topology(router):
+ """
+ Get the ISIS vrf topology in a dictionary format.
+
+ Sample:
+ {
+ 'area-name': {
+ 'level-1': [
+ {
+ 'vertex': 'r1'
+ }
+ ],
+ 'level-2': [
+ {
+ 'vertex': '10.0.0.1/24',
+ 'type': 'IP',
+ 'parent': '0',
+ 'metric': 'internal'
+ }
+ ]
+ },
+ 'area-name-2': {
+ 'level-2': [
+ {
+ "interface": "rX-ethY",
+ "metric": "Z",
+ "next-hop": "rA",
+ "parent": "rC(B)",
+ "type": "TE-IS",
+ "vertex": "rD"
+ }
+ ]
+ }
+ }
+ """
+ l1out = topotest.normalize_text(
+ router.vtysh_cmd("show isis vrf {}-cust1 topology level-1".format(router.name))
+ ).splitlines()
+ l2out = topotest.normalize_text(
+ router.vtysh_cmd("show isis vrf {}-cust1 topology level-2".format(router.name))
+ ).splitlines()
+
+ l1 = parse_topology(l1out, "level-1")
+ l2 = parse_topology(l2out, "level-2")
+
+ dict_merge(l1, l2)
+ return l1
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf b/tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..4d5fe38
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ interface r1-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf b/tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..877e14f
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/0 area 0
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..63281e9
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.1.2",
+ "ifaceName":"r1-eth0:10.0.1.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_route.ref b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..2131668
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ip_route.ref
@@ -0,0 +1,160 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0/24":[
+ {
+ "prefix":"123.0.1.0/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"123.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref
new file mode 100644
index 0000000..99a5966
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_all_binding.ref
@@ -0,0 +1,61 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..ccc8413
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_binding.ref
@@ -0,0 +1,55 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"-",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"-",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"-",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..b349f44
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_discovery.ref
@@ -0,0 +1,11 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth0",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..4bff444
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/show_ldp_neighbor.ref
@@ -0,0 +1,10 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf b/tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf
new file mode 100644
index 0000000..83aea46
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to sw0
+ ip address 10.0.1.1/24
+ ip address 123.0.1.1/24
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf b/tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..175d3d0
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/ldpd.conf
@@ -0,0 +1,28 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf b/tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..8cba152
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..f361d60
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.1.1",
+ "ifaceName":"r2-eth0:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.2.3",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "priority":3,
+ "converged":"Full",
+ "address":"10.0.2.4",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_route.ref b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..4b1d31a
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ip_route.ref
@@ -0,0 +1,198 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref
new file mode 100644
index 0000000..95fb847
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_all_binding.ref
@@ -0,0 +1,63 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..ea32de3
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_binding.ref
@@ -0,0 +1,63 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..8129570
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth0",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf b/tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf
new file mode 100644
index 0000000..1f1e3e3
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r2/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ ip address 10.0.1.2/24
+! no link-detect
+!
+interface r2-eth1
+ description to sw1
+ ip address 10.0.2.2/24
+! no link-detect
+!
+interface r2-eths2
+ description to sw2
+ ip address 10.0.3.2/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf b/tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..81bd25a
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf b/tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..0d3a74c
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/ospfd.conf
@@ -0,0 +1,13 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/0 area 0
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..3879435
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.2",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "priority":3,
+ "converged":"Full",
+ "address":"10.0.2.4",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_route.ref b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..4d115ca
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ip_route.ref
@@ -0,0 +1,198 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref
new file mode 100644
index 0000000..100dd30
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_all_binding.ref
@@ -0,0 +1,61 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..bb1b2b3
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_binding.ref
@@ -0,0 +1,62 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..c3a07e7
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_discovery.ref
@@ -0,0 +1,11 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth0",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..4bff444
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/show_ldp_neighbor.ref
@@ -0,0 +1,10 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf b/tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf
new file mode 100644
index 0000000..234c215
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ ip address 10.0.2.3/24
+! no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf b/tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf
new file mode 100644
index 0000000..9f66d7b
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r4
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 4.4.4.4
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ !
+ !interface r4-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf b/tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf
new file mode 100644
index 0000000..7bbd228
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/0 area 0
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 3
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..fccca69
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,21 @@
+
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.2",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.2.3",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_route.ref b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_route.ref
new file mode 100644
index 0000000..223cbde
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ip_route.ref
@@ -0,0 +1,186 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref
new file mode 100644
index 0000000..2a46c40
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_all_binding.ref
@@ -0,0 +1,68 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_binding.ref b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_binding.ref
new file mode 100644
index 0000000..2a46c40
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_binding.ref
@@ -0,0 +1,68 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_discovery.ref b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_discovery.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_discovery.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/show_ldp_neighbor.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf b/tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf
new file mode 100644
index 0000000..7e29105
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/r4/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r4
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ ip address 10.0.2.4/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot b/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot
new file mode 100644
index 0000000..62058e3
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.dot
@@ -0,0 +1,76 @@
+## 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="Test Topology - LDP-OC 1";
+
+ # 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
+ s0 [
+ shape=oval,
+ label="10.0.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s1 [
+ shape=oval,
+ label="10.0.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+
+ r1 -- s0 [label="eth0"];
+ r2 -- s0 [label="eth0"];
+
+ r2 -- s1 [label="eth1"];
+ r3 -- s1 [label="eth0"];
+ r4 -- s1 [label="eth0"];
+
+ r2 -- s2 [label="eth2"];
+ r3 -- s2 [label="eth1"];
+}
diff --git a/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py b/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py
new file mode 100644
index 0000000..01f3fe1
--- /dev/null
+++ b/tests/topotests/ldp_oc_acl_topo1/test_ldp_oc_acl_topo1.py
@@ -0,0 +1,258 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_oc_acl_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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.
+#
+
+r"""
+test_ldp_oc_acl_topo1.py: Simple FRR LDP Test
+
+ +---------+
+ | r1 |
+ | 1.1.1.1 |
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 |
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 |
+ | 3.3.3.3 | | 4.4.4.4 |
+ +-----------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+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.
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["r1", "r2", "r3", "r4"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s0")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start ospfd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ 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)
+
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_convergence():
+ logger.info("Test: check OSPF adjacencies")
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json"
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_ldp_bindings():
+ logger.info("Test: verify LDP bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_binding.ref"
+ )
+
+
+def test_ldp_bindings_all_routes():
+ logger.info("Test: verify LDP bindings after host filter removed")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # remove ACL that blocks advertising everything but host routes */
+ cmd = 'vtysh -c "configure terminal" -c "mpls ldp" -c "address-family ipv4" -c "no label local allocate host-routes"'
+ tgen.net["r1"].cmd(cmd)
+ sleep(2)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_all_binding.ref"
+ )
+
+
+# Memory leak test template
+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/ldp_oc_topo1/r1/ldpd.conf b/tests/topotests/ldp_oc_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..fdb9e62
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ !
+ interface r1-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r1/ospfd.conf b/tests/topotests/ldp_oc_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..877e14f
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/0 area 0
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..63281e9
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.1.2",
+ "ifaceName":"r1-eth0:10.0.1.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref b/tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..2131668
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ip_route.ref
@@ -0,0 +1,160 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0/24":[
+ {
+ "prefix":"123.0.1.0/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"123.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp_oc_topo1/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..99a5966
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ldp_binding.ref
@@ -0,0 +1,61 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp_oc_topo1/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..b349f44
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ldp_discovery.ref
@@ -0,0 +1,11 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth0",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_topo1/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..4bff444
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/show_ldp_neighbor.ref
@@ -0,0 +1,10 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r1/zebra.conf b/tests/topotests/ldp_oc_topo1/r1/zebra.conf
new file mode 100644
index 0000000..83aea46
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to sw0
+ ip address 10.0.1.1/24
+ ip address 123.0.1.1/24
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r2/ldpd.conf b/tests/topotests/ldp_oc_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..175d3d0
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/ldpd.conf
@@ -0,0 +1,28 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r2/ospfd.conf b/tests/topotests/ldp_oc_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..8cba152
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..f361d60
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.1.1",
+ "ifaceName":"r2-eth0:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.2.3",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "priority":3,
+ "converged":"Full",
+ "address":"10.0.2.4",
+ "ifaceName":"r2-eth1:10.0.2.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref b/tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..4b1d31a
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ip_route.ref
@@ -0,0 +1,198 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp_oc_topo1/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..95fb847
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ldp_binding.ref
@@ -0,0 +1,63 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp_oc_topo1/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..8129570
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth0",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_topo1/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r2/zebra.conf b/tests/topotests/ldp_oc_topo1/r2/zebra.conf
new file mode 100644
index 0000000..1f1e3e3
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r2/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ ip address 10.0.1.2/24
+! no link-detect
+!
+interface r2-eth1
+ description to sw1
+ ip address 10.0.2.2/24
+! no link-detect
+!
+interface r2-eths2
+ description to sw2
+ ip address 10.0.3.2/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r3/ldpd.conf b/tests/topotests/ldp_oc_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..81bd25a
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r3/ospfd.conf b/tests/topotests/ldp_oc_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..0d3a74c
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/ospfd.conf
@@ -0,0 +1,13 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/0 area 0
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..3879435
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.2",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "priority":3,
+ "converged":"Full",
+ "address":"10.0.2.4",
+ "ifaceName":"r3-eth0:10.0.2.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref b/tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..4d115ca
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ip_route.ref
@@ -0,0 +1,198 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp_oc_topo1/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..100dd30
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ldp_binding.ref
@@ -0,0 +1,61 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "remoteLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"2.2.2.2",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp_oc_topo1/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..c3a07e7
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ldp_discovery.ref
@@ -0,0 +1,11 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth0",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_topo1/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..4bff444
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/show_ldp_neighbor.ref
@@ -0,0 +1,10 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r3/zebra.conf b/tests/topotests/ldp_oc_topo1/r3/zebra.conf
new file mode 100644
index 0000000..234c215
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ ip address 10.0.2.3/24
+! no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r4/ldpd.conf b/tests/topotests/ldp_oc_topo1/r4/ldpd.conf
new file mode 100644
index 0000000..9f66d7b
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/ldpd.conf
@@ -0,0 +1,24 @@
+hostname r4
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 4.4.4.4
+ ordered-control
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ !
+ !interface r4-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/r4/ospfd.conf b/tests/topotests/ldp_oc_topo1/r4/ospfd.conf
new file mode 100644
index 0000000..7bbd228
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/ospfd.conf
@@ -0,0 +1,12 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/0 area 0
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 3
+!
diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..fccca69
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,21 @@
+
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.2",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.2.3",
+ "ifaceName":"r4-eth0:10.0.2.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref b/tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref
new file mode 100644
index 0000000..223cbde
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ip_route.ref
@@ -0,0 +1,186 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4/32":[
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"4.4.4.4/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "123.0.1.0\/24":[
+ {
+ "prefix":"123.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"r4-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ldp_binding.ref b/tests/topotests/ldp_oc_topo1/r4/show_ldp_binding.ref
new file mode 100644
index 0000000..2a46c40
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ldp_binding.ref
@@ -0,0 +1,68 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"4.4.4.4/32",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.2.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"10.0.3.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"123.0.1.0/24",
+ "neighborId":"0.0.0.0",
+ "localLabel":"imp-null",
+ "remoteLabel":"-",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ldp_discovery.ref b/tests/topotests/ldp_oc_topo1/r4/show_ldp_discovery.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ldp_discovery.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_oc_topo1/r4/show_ldp_neighbor.ref b/tests/topotests/ldp_oc_topo1/r4/show_ldp_neighbor.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/show_ldp_neighbor.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_oc_topo1/r4/zebra.conf b/tests/topotests/ldp_oc_topo1/r4/zebra.conf
new file mode 100644
index 0000000..7e29105
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/r4/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r4
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ ip address 10.0.2.4/24
+! no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot b/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot
new file mode 100644
index 0000000..62058e3
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.dot
@@ -0,0 +1,76 @@
+## 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="Test Topology - LDP-OC 1";
+
+ # 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
+ s0 [
+ shape=oval,
+ label="10.0.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s1 [
+ shape=oval,
+ label="10.0.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+
+ r1 -- s0 [label="eth0"];
+ r2 -- s0 [label="eth0"];
+
+ r2 -- s1 [label="eth1"];
+ r3 -- s1 [label="eth0"];
+ r4 -- s1 [label="eth0"];
+
+ r2 -- s2 [label="eth2"];
+ r3 -- s2 [label="eth1"];
+}
diff --git a/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py b/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py
new file mode 100644
index 0000000..4faaf45
--- /dev/null
+++ b/tests/topotests/ldp_oc_topo1/test_ldp_oc_topo1.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_oc_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by by Volta Networks
+#
+# 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.
+#
+
+r"""
+test_ldp_oc_topo1.py: Simple FRR LDP Test
+
+ +---------+
+ | r1 |
+ | 1.1.1.1 |
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 |
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 |
+ | 3.3.3.3 | | 4.4.4.4 |
+ +-----------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["r1", "r2", "r3", "r4"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s0")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start ospfd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ 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)
+
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_convergence():
+ logger.info("Test: check OSPF adjacencies")
+
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json"
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_ldp_bindings():
+ logger.info("Test: verify LDP bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_binding.ref"
+ )
+
+
+# Memory leak test template
+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/ldp_snmp/ce1/zebra.conf b/tests/topotests/ldp_snmp/ce1/zebra.conf
new file mode 100644
index 0000000..6f165e2
--- /dev/null
+++ b/tests/topotests/ldp_snmp/ce1/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface ce1-eth0
+ ip address 172.16.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/ce2/zebra.conf b/tests/topotests/ldp_snmp/ce2/zebra.conf
new file mode 100644
index 0000000..ac02d0f
--- /dev/null
+++ b/tests/topotests/ldp_snmp/ce2/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface ce2-eth0
+ ip address 172.16.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/ce3/zebra.conf b/tests/topotests/ldp_snmp/ce3/zebra.conf
new file mode 100644
index 0000000..c6a5824
--- /dev/null
+++ b/tests/topotests/ldp_snmp/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/r1/isisd.conf b/tests/topotests/ldp_snmp/r1/isisd.conf
new file mode 100644
index 0000000..d1abb49
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/isisd.conf
@@ -0,0 +1,27 @@
+hostname r1
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r1-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r1-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp_snmp/r1/ldpd.conf b/tests/topotests/ldp_snmp/r1/ldpd.conf
new file mode 100644
index 0000000..c13135f
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth1
+ !
+ interface r1-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r1-eth0
+ !
+ member pseudowire r1-mpw0
+ neighbor lsr-id 2.2.2.2
+ pw-id 100
+ !
+!
+line vty
+!
+agentx
+!
diff --git a/tests/topotests/ldp_snmp/r1/show_ip_route.ref b/tests/topotests/ldp_snmp/r1/show_ip_route.ref
new file mode 100644
index 0000000..b1a55ba
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_interface_detail.ref b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail.ref
new file mode 100644
index 0000000..d8fb27a
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..f77d65e
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..f77d65e
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..b699e8c
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..c28cd4c
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r1-eth1":{
+ "Interface":true
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..c63bbea
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref b/tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref
new file mode 100644
index 0000000..b3de7e2
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "2.2.2.2: 100":{
+ "destination":"2.2.2.2",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref b/tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref
new file mode 100644
index 0000000..29e9df1
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r1-mpw0":{
+ "peerId":"2.2.2.2",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_ldp_binding.ref b/tests/topotests/ldp_snmp/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..b3a12ec
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref b/tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..9301e60
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"targeted",
+ "peer":"2.2.2.2",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r1-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..54d015f
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..2232069
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..40d8ebe
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_snmp/r1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..6138d03
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r1-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r1-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r1/snmpd.conf b/tests/topotests/ldp_snmp/r1/snmpd.conf
new file mode 100644
index 0000000..3fd5e98
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/ldp_snmp/r1/zebra.conf b/tests/topotests/ldp_snmp/r1/zebra.conf
new file mode 100644
index 0000000..bbb98d2
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+! debug zebra kernel
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra nht
+! debug zebra pseudowires
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to s1
+!
+interface r1-eth1
+ description to s4
+ ip address 10.0.1.1/24
+!
+interface r1-eth2
+ description to s5
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/r2/isisd.conf b/tests/topotests/ldp_snmp/r2/isisd.conf
new file mode 100644
index 0000000..213b65e
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/isisd.conf
@@ -0,0 +1,28 @@
+hostname r2
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r2-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r2-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
diff --git a/tests/topotests/ldp_snmp/r2/ldpd.conf b/tests/topotests/ldp_snmp/r2/ldpd.conf
new file mode 100644
index 0000000..fdb76ed
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r2-eth0
+ !
+ member pseudowire r2-mpw0
+ neighbor lsr-id 1.1.1.1
+ pw-id 100
+ !
+!
+line vty
+!
+!agentx
+!
diff --git a/tests/topotests/ldp_snmp/r2/ospfd.conf b/tests/topotests/ldp_snmp/r2/ospfd.conf
new file mode 100644
index 0000000..50c593c
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+! debug ospf zebra interface
+! debug ospf ldp-sync
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf mpls ldp-sync holddown 300
+!
+interface r2-eth2
+ ip ospf network point-to-point
+ no ip ospf mpls ldp-sync
+!
diff --git a/tests/topotests/ldp_snmp/r2/show_ip_route.ref b/tests/topotests/ldp_snmp/r2/show_ip_route.ref
new file mode 100644
index 0000000..04f141a
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_interface_detail.ref b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail.ref
new file mode 100644
index 0000000..844aa94
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..821ec70
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..821ec70
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..433d89b
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..2f3eae4
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..c3d97a3
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r2-eth1":{
+ "Interface":true
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref b/tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref
new file mode 100644
index 0000000..42c5a1c
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "1.1.1.1: 100":{
+ "destination":"1.1.1.1",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref b/tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref
new file mode 100644
index 0000000..942ed23
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r2-mpw0":{
+ "peerId":"1.1.1.1",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_binding.ref b/tests/topotests/ldp_snmp/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..c641fb4
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref b/tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..26801ac
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"targeted",
+ "peer":"1.1.1.1",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..f2b24d7
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..b5508dd
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..f2b24d7
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_snmp/r2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..4dd6ddd
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r2-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r2-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r2/snmpd.conf b/tests/topotests/ldp_snmp/r2/snmpd.conf
new file mode 100644
index 0000000..fc64805
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:2.2.2.2:161
+
+com2sec public 2.2.2.2 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr \ No newline at end of file
diff --git a/tests/topotests/ldp_snmp/r2/zebra.conf b/tests/topotests/ldp_snmp/r2/zebra.conf
new file mode 100644
index 0000000..c79b210
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r2
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to s2
+!
+interface r2-eth1
+ description to s4
+ ip address 10.0.1.2/24
+!
+interface r2-eth2
+ description to s6
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/r3/isisd.conf b/tests/topotests/ldp_snmp/r3/isisd.conf
new file mode 100644
index 0000000..956d582
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/isisd.conf
@@ -0,0 +1,29 @@
+hostname r3
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
+interface r3-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp_snmp/r3/ldpd.conf b/tests/topotests/ldp_snmp/r3/ldpd.conf
new file mode 100644
index 0000000..d1a928b
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/ldpd.conf
@@ -0,0 +1,27 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
+!agentx
+!
diff --git a/tests/topotests/ldp_snmp/r3/show_ip_route.ref b/tests/topotests/ldp_snmp/r3/show_ip_route.ref
new file mode 100644
index 0000000..2250404
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_interface_detail.ref b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..9cb70a4
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..9cb70a4
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..9cb70a4
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref b/tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_l2vpn_binding.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref b/tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_l2vpn_vc.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_ldp_binding.ref b/tests/topotests/ldp_snmp/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..e54bd6e
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref b/tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..42fa98d
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r3-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..5c482da
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_snmp/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_snmp/r3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0922192
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r3-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r3-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_snmp/r3/zebra.conf b/tests/topotests/ldp_snmp/r3/zebra.conf
new file mode 100644
index 0000000..45929ac
--- /dev/null
+++ b/tests/topotests/ldp_snmp/r3/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r3
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to s3
+!
+interface r3-eth1
+ description to s5
+ ip address 10.0.2.3/24
+!
+interface r3-eth2
+ description to s6
+ ip address 10.0.3.3/24
+!
+!!interface r3-eth3
+!! description to s4
+!! ip address 10.0.1.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py b/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
new file mode 100644
index 0000000..b198f29
--- /dev/null
+++ b/tests/topotests/ldp_snmp/test_ldp_snmp_topo1.py
@@ -0,0 +1,385 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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_ldp_vpls_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---------+ +---------+
+ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
+ | |
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ 10.0.1.0/24 +---------+
+ | |rt1-eth1 | |
+ | RT1 +----------------+ RT2 |
+ | 1.1.1.1 | rt2-eth1| 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth2| |rt2-eth2
+ | |
+ | |
+ 10.0.2.0/24| +---------+ |10.0.3.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth2| |rt3-eth1
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.snmptest import SnmpTester
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.isisd, pytest.mark.snmp]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start isisd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP,
+ os.path.join(CWD, "{}/ldpd.conf".format(rname)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP,
+ os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap",
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # 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=320, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_isis_convergence():
+ logger.info("Test: check ISIS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ # TODO: disabling this check to avoid 'snmpd not running' errors
+ # if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ # TODO: disabling this check to avoid 'snmpd not running' errors
+ # if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ # if tgen.routers_have_failure():
+ # pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_r1_ldp_lsr_objects():
+ "Test mplsLdpLsrObjects objects"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid("mplsLdpLsrId", "01 01 01 01")
+ assert r1_snmp.test_oid("mplsLdpLsrLoopDetectionCapable", "none(1)")
+
+
+def test_r1_ldp_entity_table():
+ "Test mplsLdpEntityTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk("mplsLdpEntityLdpId", ["1.1.1.1:0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityIndex", ["1"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityProtocolVersion", ["1"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityAdminStatus", ["enable(1)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityOperStatus", ["enabled(2)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityTcpPort", ["646"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityUdpDscPort", ["646"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityMaxPduLength", ["4096 octets"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityKeepAliveHoldTimer", ["180 seconds"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityHelloHoldTimer", ["0 seconds"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityInitSessionThreshold", ["0"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpEntityLabelDistMethod", ["downstreamUnsolicited(2)"]
+ )
+ assert r1_snmp.test_oid_walk("mplsLdpEntityLabelRetentionMode", ["liberal(2)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityPathVectorLimit", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityHopCountLimit", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityTransportAddrKind", ["loopback(2)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeer", ["true(1)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeerAddrType", ["ipv4(1)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityTargetPeerAddr", ["01 01 01 01"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityLabelType", ["generic(1)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityDiscontinuityTime", ["(0) 0:00:00.00"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStorageType", ["nonVolatile(3)"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityRowStatus", ["createAndGo(4)"])
+
+
+def test_r1_ldp_entity_stats_table():
+ "Test mplsLdpEntityStatsTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionAttempts", ["0"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpEntityStatsSessionRejectedNoHelloErrors", ["0"]
+ )
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedAdErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedMaxPduErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsSessionRejectedLRErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadLdpIdentifierErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadPduLengthErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadMessageLengthErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsBadTlvLengthErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsMalformedTlvValueErrors", ["0"])
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsKeepAliveTimerExpErrors", ["0"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpEntityStatsShutdownReceivedNotifications", ["0"]
+ )
+ assert r1_snmp.test_oid_walk("mplsLdpEntityStatsShutdownSentNotifications", ["0"])
+
+
+def test_r1_ldp_peer_table():
+ "Test mplsLdpPeerTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk("mplsLdpPeerLdpId", ["2.2.2.2:0", "3.3.3.3:0"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpPeerLabelDistMethod",
+ ["downstreamUnsolicited(2)", "downstreamUnsolicited(2)"],
+ )
+ assert r1_snmp.test_oid_walk("mplsLdpPeerPathVectorLimit", ["0", "0"])
+ assert r1_snmp.test_oid_walk("mplsLdpPeerTransportAddrType", ["ipv4(1)", "ipv4(1)"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpPeerTransportAddr", ["02 02 02 02", "03 03 03 03"]
+ )
+
+
+def test_r1_ldp_session_table():
+ "Test mplsLdpSessionTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpSessionState", ["operational(5)", "operational(5)"]
+ )
+ assert r1_snmp.test_oid_walk("mplsLdpSessionRole", ["passive(3)", "passive(3)"])
+ assert r1_snmp.test_oid_walk("mplsLdpSessionProtocolVersion", ["1", "1"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpSessionKeepAliveTime", ["180 seconds", "180 seconds"]
+ )
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpSessionMaxPduLength", ["4096 octets", "4096 octets"]
+ )
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpSessionDiscontinuityTime", ["(0) 0:00:00.00", "(0) 0:00:00.00"]
+ )
+
+
+def test_r1_ldp_session_stats_table():
+ "Test mplsLdpSessionStatsTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk("mplsLdpSessionStatsUnknownMesTypeErrors", ["0", "0"])
+ assert r1_snmp.test_oid_walk("mplsLdpSessionStatsUnknownTlvErrors", ["0", "0"])
+
+
+def test_r1_ldp_hello_adjacency_table():
+ "Test mplsLdpHelloAdjacencyTable"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+
+ assert r1_snmp.test_oid_walk("mplsLdpHelloAdjacencyIndex", ["1", "2", "1"])
+ assert r1_snmp.test_oid_walk("mplsLdpHelloAdjacencyHoldTime", ["15", "45", "15"])
+ assert r1_snmp.test_oid_walk(
+ "mplsLdpHelloAdjacencyType", ["link(1)", "targeted(2)", "link(1)"]
+ )
+
+
+# Memory leak test template
+# disabling memory leak
+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/ldp_sync_isis_topo1/ce1/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/ce1/zebra.conf
new file mode 100644
index 0000000..6f165e2
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/ce1/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface ce1-eth0
+ ip address 172.16.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf
new file mode 100644
index 0000000..ac02d0f
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/ce2/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface ce2-eth0
+ ip address 172.16.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf
new file mode 100644
index 0000000..c6a5824
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf
new file mode 100644
index 0000000..d1abb49
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/isisd.conf
@@ -0,0 +1,27 @@
+hostname r1
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0001.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r1-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r1-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf b/tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..973acf4
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/ldpd.conf
@@ -0,0 +1,33 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth1
+ !
+ interface r1-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r1-eth0
+ !
+ member pseudowire r1-mpw0
+ neighbor lsr-id 2.2.2.2
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ip_route.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..b1a55ba
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail.ref
new file mode 100644
index 0000000..d8fb27a
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..f77d65e
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..f77d65e
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r1-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r1-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..b699e8c
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..c28cd4c
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r1-eth1":{
+ "Interface":true
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..c63bbea
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref
new file mode 100644
index 0000000..b3de7e2
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "2.2.2.2: 100":{
+ "destination":"2.2.2.2",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref
new file mode 100644
index 0000000..29e9df1
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r1-mpw0":{
+ "peerId":"2.2.2.2",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..b3a12ec
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..9301e60
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"targeted",
+ "peer":"2.2.2.2",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r1-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..54d015f
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..2232069
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..40d8ebe
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_sync_isis_topo1/r1/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..6138d03
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r1-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r1-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf
new file mode 100644
index 0000000..bbb98d2
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+! debug zebra kernel
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra nht
+! debug zebra pseudowires
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to s1
+!
+interface r1-eth1
+ description to s4
+ ip address 10.0.1.1/24
+!
+interface r1-eth2
+ description to s5
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf
new file mode 100644
index 0000000..213b65e
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/isisd.conf
@@ -0,0 +1,28 @@
+hostname r2
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0002.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+!
+interface r2-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
+interface r2-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf b/tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..e738ff9
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/ldpd.conf
@@ -0,0 +1,33 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r2-eth0
+ !
+ member pseudowire r2-mpw0
+ neighbor lsr-id 1.1.1.1
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ip_route.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..04f141a
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail.ref
new file mode 100644
index 0000000..844aa94
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..821ec70
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..821ec70
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r2-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "16777214"
+ }
+ ],
+ "r2-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..433d89b
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..2f3eae4
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..c3d97a3
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "r2-eth1":{
+ "Interface":true
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref
new file mode 100644
index 0000000..42c5a1c
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "1.1.1.1: 100":{
+ "destination":"1.1.1.1",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref
new file mode 100644
index 0000000..942ed23
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r2-mpw0":{
+ "peerId":"1.1.1.1",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..c641fb4
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..26801ac
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"targeted",
+ "peer":"1.1.1.1",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..f2b24d7
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..b5508dd
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..f2b24d7
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_igp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_sync_isis_topo1/r2/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..4dd6ddd
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r2-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r2-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0003",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf
new file mode 100644
index 0000000..c79b210
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r2
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to s2
+!
+interface r2-eth1
+ description to s4
+ ip address 10.0.1.2/24
+!
+interface r2-eth2
+ description to s6
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf b/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf
new file mode 100644
index 0000000..956d582
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/isisd.conf
@@ -0,0 +1,29 @@
+hostname r3
+log file isisd.log
+! debug isis adj-packets
+! debug isis events
+! debug isis update-packets
+! debug isis ldp-sync
+!
+router isis 1
+ lsp-gen-interval 2
+ net 10.0000.0000.0000.0000.0000.0000.0000.0000.0003.00
+ metric-style wide
+ redistribute ipv4 connected level-1
+ redistribute ipv6 connected level-1
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r3-eth1
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+ no isis mpls ldp-sync
+!
+interface r3-eth2
+ ip router isis 1
+ ipv6 router isis 1
+ isis network point-to-point
+ isis circuit-type level-1
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf b/tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..fae25e0
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ip_route.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..2250404
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ip_route.ref
@@ -0,0 +1,134 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"isis",
+ "selected":true,
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"isis",
+ "distance":115,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..e323f61
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_interface_detail_r2_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "1": {
+ "r3-eth1": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ],
+ "r3-eth2": [
+ {
+ "level": "Level-1",
+ "metric": "10"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref
new file mode 100644
index 0000000..7180f84
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..7180f84
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..7180f84
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_isis_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,13 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_binding.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_l2vpn_vc.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..e54bd6e
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..42fa98d
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r3-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..5c482da
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/show_yang_interface_isis_adjacencies.ref b/tests/topotests/ldp_sync_isis_topo1/r3/show_yang_interface_isis_adjacencies.ref
new file mode 100644
index 0000000..0922192
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/show_yang_interface_isis_adjacencies.ref
@@ -0,0 +1,42 @@
+{
+ "frr-interface:lib": {
+ "interface": [
+ {
+ "name": "r3-eth1",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0001",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ },
+ {
+ "name": "r3-eth2",
+ "vrf": "default",
+ "state": {
+ "frr-isisd:isis": {
+ "adjacencies": {
+ "adjacency": [
+ {
+ "neighbor-sys-type": "level-1",
+ "neighbor-sysid": "0000.0000.0002",
+ "neighbor-extended-circuit-id": 0,
+ "state": "up"
+ }
+ ]
+ }
+ }
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf b/tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf
new file mode 100644
index 0000000..45929ac
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/r3/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r3
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to s3
+!
+interface r3-eth1
+ description to s5
+ ip address 10.0.2.3/24
+!
+interface r3-eth2
+ description to s6
+ ip address 10.0.3.3/24
+!
+!!interface r3-eth3
+!! description to s4
+!! ip address 10.0.1.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.dot b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.dot
new file mode 100644
index 0000000..4f1bd22
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.dot
@@ -0,0 +1,111 @@
+## 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="Test Topology - LDP-VPLS 1";
+
+ # Routers
+ ce1 [
+ shape=doubleoctagon,
+ label="ce1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce2 [
+ shape=doubleoctagon
+ label="ce2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce3 [
+ shape=doubleoctagon
+ label="ce3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ 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,
+ ];
+
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="s4\n10.0.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ shape=oval,
+ label="s5\n10.0.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ shape=oval,
+ label="s6\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ ce1 -- s1 [label="eth0\n.1"];
+ ce2 -- s2 [label="eth0\n.2"];
+ ce3 -- s3 [label="eth0\n.3"];
+
+ r1 -- s1 [label="eth0"];
+ r1 -- s4 [label="eth1\n.1"];
+ r1 -- s5 [label="eth2\n.1"];
+
+ r2 -- s2 [label="eth0"];
+ r2 -- s4 [label="eth1\n.2"];
+ r2 -- s6 [label="eth2\n.2"];
+
+ r3 -- s3 [label="eth0"];
+ r3 -- s5 [label="eth1\n.3"];
+ r3 -- s6 [label="eth2\n.3"];
+}
diff --git a/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
new file mode 100644
index 0000000..48584f0
--- /dev/null
+++ b/tests/topotests/ldp_sync_isis_topo1/test_ldp_sync_isis_topo1.py
@@ -0,0 +1,626 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_isis_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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_ldp_vpls_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---------+ +---------+
+ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
+ | |
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ 10.0.1.0/24 +---------+
+ | |rt1-eth1 | |
+ | RT1 +----------------+ RT2 |
+ | 1.1.1.1 | rt2-eth1| 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth2| |rt2-eth2
+ | |
+ | |
+ 10.0.2.0/24| +---------+ |10.0.3.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth2| |rt3-eth1
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.isisd, pytest.mark.ldpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start isisd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # 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=320, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_isis_convergence():
+ logger.info("Test: check ISIS adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show yang operational-data /frr-interface:lib isisd",
+ "show_yang_interface_isis_adjacencies.ref",
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_ldp_bindings():
+ logger.info("Test: verify LDP bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_binding.ref"
+ )
+
+
+def test_ldp_pwid_bindings():
+ logger.info("Test: verify LDP PW-ID bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref"
+ )
+
+
+def test_ldp_pseudowires():
+ logger.info("Test: verify LDP pseudowires")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ )
+
+
+def test_ldp_igp_sync():
+ logger.info("Test: verify LDP igp-sync")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+
+def test_isis_ldp_sync():
+ logger.info("Test: verify ISIS igp-sync")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_interface_detail(
+ rname, "show_isis_interface_detail.ref"
+ )
+ assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_r1_eth1_shutdown():
+ logger.info("Test: verify behaviour after r1-eth1 is shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Shut down r1-r2 link */
+ tgen = get_topogen()
+ 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)
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show mpls ldp igp-sync json",
+ "show_ldp_igp_sync_r1_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_ldp_sync(
+ rname, "show_isis_ldp_sync_r1_eth1_shutdown.ref"
+ )
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_interface_detail(
+ rname, "show_isis_interface_detail_r1_eth1_shutdown.ref"
+ )
+ assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_r1_eth1_no_shutdown():
+ logger.info("Test: verify behaviour after r1-eth1 is no shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Run no shutdown on r1-eth1 interface */
+ tgen = get_topogen()
+ tgen.gears["r1"].peer_link_enable("r1-eth1", True)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_interface_detail(
+ rname, "show_isis_interface_detail.ref"
+ )
+ assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_r2_eth1_shutdown():
+ logger.info("Test: verify behaviour after r2-eth1 is shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Shut down r1-r2 link */
+ tgen = get_topogen()
+ tgen.gears["r2"].peer_link_enable("r2-eth1", False)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show mpls ldp igp-sync json",
+ "show_ldp_igp_sync_r1_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_ldp_sync(
+ rname, "show_isis_ldp_sync_r2_eth1_shutdown.ref"
+ )
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_interface_detail(
+ rname, "show_isis_interface_detail_r2_eth1_shutdown.ref"
+ )
+ assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
+
+
+def test_r2_eth1_no_shutdown():
+ logger.info("Test: verify behaviour after r2-eth1 is no shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Run no shutdown on r2-eth1 interface */
+ tgen = get_topogen()
+ tgen.gears["r2"].peer_link_enable("r2-eth1", True)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_ldp_sync(rname, "show_isis_ldp_sync.ref")
+ assert result, "ISIS did not converge on {}:\n{}".format(rname, diff)
+
+ for rname in ["r1", "r2", "r3"]:
+ (result, diff) = validate_show_isis_interface_detail(
+ rname, "show_isis_interface_detail.ref"
+ )
+ assert result, "ISIS interface did not converge on {}:\n{}".format(rname, diff)
+
+
+# Memory leak test template
+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))
+
+
+#
+# Auxiliary functions
+#
+
+
+def parse_show_isis_ldp_sync(lines, rname):
+ """
+ Parse the output of 'show isis mpls ldp sync' into a Python dict.
+ """
+ interfaces = {}
+
+ it = iter(lines)
+
+ while True:
+ try:
+ interface = {}
+ interface_name = None
+
+ line = next(it)
+
+ if line.startswith(rname + "-eth"):
+ interface_name = line
+
+ line = next(it)
+
+ if line.startswith(" LDP-IGP Synchronization enabled: "):
+ interface["ldpIgpSyncEnabled"] = line.endswith("yes")
+ line = next(it)
+
+ if line.startswith(" holddown timer in seconds: "):
+ interface["holdDownTimeInSec"] = int(line.split(": ")[-1])
+ line = next(it)
+
+ if line.startswith(" State: "):
+ interface["ldpIgpSyncState"] = line.split(": ")[-1]
+
+ elif line.startswith(" Interface "):
+ interface["Interface"] = line.endswith("down")
+
+ interfaces[interface_name] = interface
+
+ except StopIteration:
+ break
+
+ return interfaces
+
+
+def show_isis_ldp_sync(router, rname):
+ """
+ Get the show isis mpls ldp-sync info in a dictionary format.
+
+ """
+ out = topotest.normalize_text(
+ router.vtysh_cmd("show isis mpls ldp-sync")
+ ).splitlines()
+
+ parsed = parse_show_isis_ldp_sync(out, rname)
+
+ return parsed
+
+
+def validate_show_isis_ldp_sync(rname, fname):
+ tgen = get_topogen()
+
+ filename = "{0}/{1}/{2}".format(CWD, rname, fname)
+ expected = json.loads(open(filename).read())
+
+ router = tgen.gears[rname]
+
+ def compare_isis_ldp_sync(router, expected):
+ "Helper function to test show isis mpls ldp-sync"
+ actual = show_isis_ldp_sync(router, rname)
+ return topotest.json_cmp(actual, expected)
+
+ test_func = partial(compare_isis_ldp_sync, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160)
+
+ return (result, diff)
+
+
+def parse_show_isis_interface_detail(lines, rname):
+ """
+ Parse the output of 'show isis interface detail' into a Python dict.
+ """
+ areas = {}
+ area_id = None
+
+ it = iter(lines)
+
+ while True:
+ try:
+ line = next(it)
+
+ area_match = re.match(r"Area (.+):", line)
+ if not area_match:
+ continue
+
+ area_id = area_match.group(1)
+ area = {}
+
+ line = next(it)
+
+ while line.startswith(" Interface: "):
+ interface_name = re.split(":|,", line)[1].lstrip()
+
+ area[interface_name] = []
+
+ # Look for keyword: Level-1 or Level-2
+ while not line.startswith(" Level-"):
+ line = next(it)
+
+ while line.startswith(" Level-"):
+
+ level = {}
+
+ level_name = line.split()[0]
+ level["level"] = level_name
+
+ line = next(it)
+
+ if line.startswith(" Metric:"):
+ level["metric"] = re.split(":|,", line)[1].lstrip()
+
+ area[interface_name].append(level)
+
+ # Look for keyword: Level-1 or Level-2 or Interface:
+ while not line.startswith(" Level-") and not line.startswith(
+ " Interface: "
+ ):
+ line = next(it)
+
+ if line.startswith(" Level-"):
+ continue
+
+ if line.startswith(" Interface: "):
+ break
+
+ areas[area_id] = area
+
+ except StopIteration:
+
+ areas[area_id] = area
+ break
+
+ return areas
+
+
+def show_isis_interface_detail(router, rname):
+ """
+ Get the show isis mpls ldp-sync info in a dictionary format.
+
+ """
+ out = topotest.normalize_text(
+ router.vtysh_cmd("show isis interface detail")
+ ).splitlines()
+
+ logger.warning(out)
+
+ parsed = parse_show_isis_interface_detail(out, rname)
+
+ logger.warning(parsed)
+
+ return parsed
+
+
+def validate_show_isis_interface_detail(rname, fname):
+ tgen = get_topogen()
+
+ filename = "{0}/{1}/{2}".format(CWD, rname, fname)
+ expected = json.loads(open(filename).read())
+
+ router = tgen.gears[rname]
+
+ def compare_isis_interface_detail(router, expected):
+ "Helper function to test show isis interface detail"
+ actual = show_isis_interface_detail(router, rname)
+ return topotest.json_cmp(actual, expected)
+
+ test_func = partial(compare_isis_interface_detail, router, expected)
+ (result, diff) = topotest.run_and_expect(test_func, None, wait=0.5, count=160)
+
+ return (result, diff)
diff --git a/tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf
new file mode 100644
index 0000000..6f165e2
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/ce1/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface ce1-eth0
+ ip address 172.16.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf
new file mode 100644
index 0000000..ac02d0f
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/ce2/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface ce2-eth0
+ ip address 172.16.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf
new file mode 100644
index 0000000..c6a5824
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf b/tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..973acf4
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/ldpd.conf
@@ -0,0 +1,33 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r1-eth1
+ !
+ interface r1-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r1-eth0
+ !
+ member pseudowire r1-mpw0
+ neighbor lsr-id 2.2.2.2
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt b/tests/topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/ospf-nbrs.txt
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf b/tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..dc20169
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/ospfd.conf
@@ -0,0 +1,20 @@
+hostname r1
+log file ospfd.log
+! debug ospf zebra interface
+! debug ospf ldp-sync
+!
+router ospf
+ router-id 1.1.1.1
+ network 10.0.1.1/24 area 0
+ network 10.0.2.1/24 area 0
+ network 1.1.1.1/32 area 0
+ mpls ldp-sync
+ ! mpls ldp-sync holddown 50
+!
+interface r1-eth1
+ ip ospf network point-to-point
+ ! ip ospf mpls ldp-sync holddown 40
+!
+interface r1-eth2
+ ip ospf network point-to-point
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface.ref
new file mode 100644
index 0000000..8b28847
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r1-eth1":{
+ "cost":10
+ },
+ "r1-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..b1a263e
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r1_eth1_shutdown.ref
@@ -0,0 +1,8 @@
+{
+ "interfaces":{
+ "r1-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..0c14733
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_interface_r2_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r1-eth1":{
+ "cost":65535
+ },
+ "r1-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..7efde22
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.1.2",
+ "ifaceName": "r1-eth1:10.0.1.1",
+ "requestCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.3",
+ "ifaceName": "r1-eth2:10.0.2.1",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_route.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..fdb81f2
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref
new file mode 100644
index 0000000..b3de7e2
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "2.2.2.2: 100":{
+ "destination":"2.2.2.2",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref
new file mode 100644
index 0000000..29e9df1
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r1-mpw0":{
+ "peerId":"2.2.2.2",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..b3a12ec
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..9301e60
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"targeted",
+ "peer":"2.2.2.2",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r1-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..54d015f
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..2232069
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r1-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r1-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..40d8ebe
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref
new file mode 100644
index 0000000..3782071
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync.ref
@@ -0,0 +1,12 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..c2642c6
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,7 @@
+{
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..6f180b0
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/show_ospf_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,12 @@
+{
+ "r1-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync not achieved"
+ },
+ "r1-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":0,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf
new file mode 100644
index 0000000..bbb98d2
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+! debug zebra kernel
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra nht
+! debug zebra pseudowires
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to s1
+!
+interface r1-eth1
+ description to s4
+ ip address 10.0.1.1/24
+!
+interface r1-eth2
+ description to s5
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf b/tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..e738ff9
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/ldpd.conf
@@ -0,0 +1,33 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r2-eth0
+ !
+ member pseudowire r2-mpw0
+ neighbor lsr-id 1.1.1.1
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf b/tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..50c593c
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+! debug ospf zebra interface
+! debug ospf ldp-sync
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf mpls ldp-sync holddown 300
+!
+interface r2-eth2
+ ip ospf network point-to-point
+ no ip ospf mpls ldp-sync
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface.ref
new file mode 100644
index 0000000..8280672
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r2-eth1":{
+ "cost":10
+ },
+ "r2-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..71e8af1
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r1_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r2-eth1":{
+ "cost":65535
+ },
+ "r2-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..af9a9c8
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_interface_r2_eth1_shutdown.ref
@@ -0,0 +1,8 @@
+{
+ "interfaces":{
+ "r2-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..5bea193
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.1.1",
+ "ifaceName":"r2-eth1:10.0.1.2",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.3.3",
+ "ifaceName":"r2-eth2:10.0.3.2",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_route.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..6056fef
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref
new file mode 100644
index 0000000..42c5a1c
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "1.1.1.1: 100":{
+ "destination":"1.1.1.1",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref
new file mode 100644
index 0000000..942ed23
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r2-mpw0":{
+ "peerId":"1.1.1.1",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..c641fb4
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..26801ac
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"targeted",
+ "peer":"1.1.1.1",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..f2b24d7
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..b5508dd
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r2-eth1":{
+ "state":"labelExchangeNotComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":""
+ },
+ "r2-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"3.3.3.3"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref
new file mode 100644
index 0000000..6c27a10
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync.ref
@@ -0,0 +1,12 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":300,
+ "ldpIgpSyncState":"Sync achieved"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..889f69e
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,12 @@
+{
+ "r2-eth1":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":300,
+ "ldpIgpSyncState":"Holding down until Sync"
+ },
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..d9036e1
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/show_ospf_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,7 @@
+{
+ "r2-eth2":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf
new file mode 100644
index 0000000..c79b210
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r2
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to s2
+!
+interface r2-eth1
+ description to s4
+ ip address 10.0.1.2/24
+!
+interface r2-eth2
+ description to s6
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf b/tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..fae25e0
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp sync
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ label local allocate host-routes
+ !
+ ttl-security disable
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf b/tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..b641fd8
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/ospfd.conf
@@ -0,0 +1,18 @@
+hostname r3
+log file ospfd.log
+! debug ospf zebra interface
+! debug ospf ldp-sync
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/0 area 0
+ mpls ldp-sync
+ mpls ldp-sync holddown 50
+!
+interface r3-eth1
+ ip ospf network point-to-point
+ no ip ospf mpls ldp-sync
+!
+interface r3-eth2
+ ip ospf network point-to-point
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface.ref
new file mode 100644
index 0000000..aec97b3
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r3-eth1":{
+ "cost":10
+ },
+ "r3-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..aec97b3
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r1_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r3-eth1":{
+ "cost":10
+ },
+ "r3-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..aec97b3
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_interface_r2_eth1_shutdown.ref
@@ -0,0 +1,11 @@
+{
+ "interfaces":{
+ "r3-eth1":{
+ "cost":10
+ },
+ "r3-eth2":{
+ "cost":10
+ }
+ }
+}
+
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..9966297
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.1",
+ "ifaceName":"r3-eth1:10.0.2.3",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "2.2.2.2": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.3.2",
+ "ifaceName":"r3-eth2:10.0.3.3",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_route.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..fc96ada
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_binding.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_l2vpn_vc.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..e54bd6e
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..42fa98d
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r3-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..7326183
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_igp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,16 @@
+{
+ "r3-eth1":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"1.1.1.1"
+ },
+ "r3-eth2":{
+ "state":"labelExchangeComplete",
+ "waitTime":10,
+ "waitTimeRemaining":0,
+ "timerRunning":false,
+ "peerLdpId":"2.2.2.2"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..5c482da
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref
new file mode 100644
index 0000000..b417ab0
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync.ref
@@ -0,0 +1,12 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref
new file mode 100644
index 0000000..b417ab0
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r1_eth1_shutdown.ref
@@ -0,0 +1,12 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref
new file mode 100644
index 0000000..b417ab0
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/show_ospf_ldp_sync_r2_eth1_shutdown.ref
@@ -0,0 +1,12 @@
+{
+ "r3-eth1":{
+ "ldpIgpSyncEnabled":false,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync not required"
+ },
+ "r3-eth2":{
+ "ldpIgpSyncEnabled":true,
+ "holdDownTimeInSec":50,
+ "ldpIgpSyncState":"Sync achieved"
+ }
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf b/tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf
new file mode 100644
index 0000000..45929ac
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/r3/zebra.conf
@@ -0,0 +1,32 @@
+log file zebra.log
+!
+hostname r3
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to s3
+!
+interface r3-eth1
+ description to s5
+ ip address 10.0.2.3/24
+!
+interface r3-eth2
+ description to s6
+ ip address 10.0.3.3/24
+!
+!!interface r3-eth3
+!! description to s4
+!! ip address 10.0.1.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.dot b/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.dot
new file mode 100644
index 0000000..4f1bd22
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.dot
@@ -0,0 +1,111 @@
+## 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="Test Topology - LDP-VPLS 1";
+
+ # Routers
+ ce1 [
+ shape=doubleoctagon,
+ label="ce1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce2 [
+ shape=doubleoctagon
+ label="ce2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce3 [
+ shape=doubleoctagon
+ label="ce3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ 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,
+ ];
+
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="s4\n10.0.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ shape=oval,
+ label="s5\n10.0.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ shape=oval,
+ label="s6\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ ce1 -- s1 [label="eth0\n.1"];
+ ce2 -- s2 [label="eth0\n.2"];
+ ce3 -- s3 [label="eth0\n.3"];
+
+ r1 -- s1 [label="eth0"];
+ r1 -- s4 [label="eth1\n.1"];
+ r1 -- s5 [label="eth2\n.1"];
+
+ r2 -- s2 [label="eth0"];
+ r2 -- s4 [label="eth1\n.2"];
+ r2 -- s6 [label="eth2\n.2"];
+
+ r3 -- s3 [label="eth0"];
+ r3 -- s5 [label="eth1\n.3"];
+ r3 -- s6 [label="eth2\n.3"];
+}
diff --git a/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py b/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py
new file mode 100644
index 0000000..dc6e1a7
--- /dev/null
+++ b/tests/topotests/ldp_sync_ospf_topo1/test_ldp_sync_ospf_topo1.py
@@ -0,0 +1,443 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# 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_ldp_vpls_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---------+ +---------+
+ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
+ | |
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ 10.0.1.0/24 +---------+
+ | |rt1-eth1 | |
+ | RT1 +----------------+ RT2 |
+ | 1.1.1.1 | rt2-eth1| 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth2| |rt2-eth2
+ | |
+ | |
+ 10.0.2.0/24| +---------+ |10.0.3.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth2| |rt3-eth1
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start ospfd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # 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=320, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_convergence():
+ logger.info("Test: check OSPF adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json"
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_ldp_bindings():
+ logger.info("Test: verify LDP bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_binding.ref"
+ )
+
+
+def test_ldp_pwid_bindings():
+ logger.info("Test: verify LDP PW-ID bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref"
+ )
+
+
+def test_ldp_pseudowires():
+ logger.info("Test: verify LDP pseudowires")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ )
+
+
+def test_ldp_igp_sync():
+ logger.info("Test: verify LDP igp-sync")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+
+def test_ospf_ldp_sync():
+ logger.info("Test: verify OSPF igp-sync")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf interface json", "show_ip_ospf_interface.ref"
+ )
+
+
+def test_r1_eth1_shutdown():
+ logger.info("Test: verify behaviour after r1-eth1 is shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Shut down r1-r2 link */
+ tgen = get_topogen()
+ 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)
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show mpls ldp igp-sync json",
+ "show_ldp_igp_sync_r1_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show ip ospf mpls ldp-sync json",
+ "show_ospf_ldp_sync_r1_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show ip ospf interface json",
+ "show_ip_ospf_interface_r1_eth1_shutdown.ref",
+ )
+
+
+def test_r1_eth1_no_shutdown():
+ logger.info("Test: verify behaviour after r1-eth1 is no shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Run no shutdown on r1-eth1 interface */
+ tgen = get_topogen()
+ tgen.gears["r1"].peer_link_enable("r1-eth1", True)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf interface json", "show_ip_ospf_interface.ref"
+ )
+
+
+def test_r2_eth1_shutdown():
+ logger.info("Test: verify behaviour after r2-eth1 is shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Shut down r1-r2 link */
+ tgen = get_topogen()
+ tgen.gears["r2"].peer_link_enable("r2-eth1", False)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show mpls ldp igp-sync json",
+ "show_ldp_igp_sync_r1_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show ip ospf mpls ldp-sync json",
+ "show_ospf_ldp_sync_r2_eth1_shutdown.ref",
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname,
+ "show ip ospf interface json",
+ "show_ip_ospf_interface_r2_eth1_shutdown.ref",
+ )
+
+
+def test_r2_eth1_no_shutdown():
+ logger.info("Test: verify behaviour after r2-eth1 is no shutdown")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Run no shutdown on r2-eth1 interface */
+ tgen = get_topogen()
+ tgen.gears["r2"].peer_link_enable("r2-eth1", True)
+ topotest.sleep(5, "Waiting for the network to reconverge")
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp igp-sync json", "show_ldp_igp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf mpls ldp-sync json", "show_ospf_ldp_sync.ref"
+ )
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf interface json", "show_ip_ospf_interface.ref"
+ )
+
+
+# Memory leak test template
+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/ldp_topo1/r1/ip_mpls_route.ref b/tests/topotests/ldp_topo1/r1/ip_mpls_route.ref
new file mode 100644
index 0000000..a13c1d4
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/ip_mpls_route.ref
@@ -0,0 +1,6 @@
+xx as to xx via inet 10.0.1.2 dev r1-eth0 proto xx
+xx as to xx via inet 10.0.1.2 dev r1-eth0 proto xx
+xx via inet 10.0.1.2 dev r1-eth0 proto xx
+xx via inet 10.0.1.2 dev r1-eth0 proto xx
+xx via inet 10.0.1.2 dev r1-eth0 proto xx
+
diff --git a/tests/topotests/ldp_topo1/r1/ldpd.conf b/tests/topotests/ldp_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..f7f2714
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ !
+ interface r1-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r1/ospfd.conf b/tests/topotests/ldp_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..87d5703
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/ospfd.conf
@@ -0,0 +1,11 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/0 area 0
+!
+int r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..d9192f1
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,14 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.1.2",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_topo1/r1/show_ipv4_route.ref b/tests/topotests/ldp_topo1/r1/show_ipv4_route.ref
new file mode 100644
index 0000000..7d39888
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_ipv4_route.ref
@@ -0,0 +1,7 @@
+O 1.1.1.1/32 [110/0] is directly connected, lo, weight 1
+O>* 2.2.2.2/32 [110/10] via 10.0.1.2, r1-eth0, label implicit-null, weight 1
+O>* 3.3.3.3/32 [110/20] via 10.0.1.2, r1-eth0, label xxx, weight 1
+O>* 4.4.4.4/32 [110/20] via 10.0.1.2, r1-eth0, label xxx, weight 1
+O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1
+O>* 10.0.2.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null, weight 1
+O>* 10.0.3.0/24 [110/20] via 10.0.1.2, r1-eth0, label implicit-null, weight 1
diff --git a/tests/topotests/ldp_topo1/r1/show_mpls_ldp_binding.ref b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_binding.ref
new file mode 100644
index 0000000..32aa60c
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_binding.ref
@@ -0,0 +1,8 @@
+AF Destination Nexthop Local Label Remote Label In Use
+ipv4 1.1.1.1/32 2.2.2.2 imp-null xxx no
+ipv4 2.2.2.2/32 2.2.2.2 xxx imp-null yes
+ipv4 3.3.3.3/32 2.2.2.2 xxx xxx yes
+ipv4 4.4.4.4/32 2.2.2.2 xxx xxx yes
+ipv4 10.0.1.0/24 2.2.2.2 imp-null imp-null no
+ipv4 10.0.2.0/24 2.2.2.2 xxx imp-null yes
+ipv4 10.0.3.0/24 2.2.2.2 xxx imp-null yes
diff --git a/tests/topotests/ldp_topo1/r1/show_mpls_ldp_discovery.ref b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_discovery.ref
new file mode 100644
index 0000000..373755a
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_discovery.ref
@@ -0,0 +1,2 @@
+AF ID Type Source Holdtime
+ipv4 2.2.2.2 Link r1-eth0 15
diff --git a/tests/topotests/ldp_topo1/r1/show_mpls_ldp_interface.ref b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_interface.ref
new file mode 100644
index 0000000..0fb15d2
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_interface.ref
@@ -0,0 +1,2 @@
+AF Interface State Uptime Hello Timers ac
+ipv4 r1-eth0 ACTIVE xx:xx:xx 5/15 1
diff --git a/tests/topotests/ldp_topo1/r1/show_mpls_ldp_neighbor.ref b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_neighbor.ref
new file mode 100644
index 0000000..29e264f
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_mpls_ldp_neighbor.ref
@@ -0,0 +1,2 @@
+AF ID State Remote Address Uptime
+ipv4 2.2.2.2 OPERATIONAL 2.2.2.2 xx:xx:xx
diff --git a/tests/topotests/ldp_topo1/r1/show_mpls_table.ref b/tests/topotests/ldp_topo1/r1/show_mpls_table.ref
new file mode 100644
index 0000000..7e24359
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/show_mpls_table.ref
@@ -0,0 +1,8 @@
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------
+ XX LDP 10.0.1.2 XX
+ XX LDP 10.0.1.2 XX
+ XX LDP 10.0.1.2 implicit-null
+ XX LDP 10.0.1.2 implicit-null
+ XX LDP 10.0.1.2 implicit-null
+
diff --git a/tests/topotests/ldp_topo1/r1/zebra.conf b/tests/topotests/ldp_topo1/r1/zebra.conf
new file mode 100644
index 0000000..55b4b0e
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r1
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to sw0
+ ip address 10.0.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r2/ip_mpls_route.ref b/tests/topotests/ldp_topo1/r2/ip_mpls_route.ref
new file mode 100644
index 0000000..f962070
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/ip_mpls_route.ref
@@ -0,0 +1,3 @@
+xx proto xx nexthopvia inet 10.0.2.3 dev r2-eth1 nexthopvia inet 10.0.3.3 dev r2-eth2
+xx via inet 10.0.1.1 dev r2-eth0 proto xx
+xx via inet 10.0.2.4 dev r2-eth1 proto xx
diff --git a/tests/topotests/ldp_topo1/r2/ldpd.conf b/tests/topotests/ldp_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..c4056e0
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/ldpd.conf
@@ -0,0 +1,25 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ !
+ interface r2-eth0
+ !
+ interface r2-eth1
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r2/ospfd.conf b/tests/topotests/ldp_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..dbed618
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/ospfd.conf
@@ -0,0 +1,19 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..ea78592
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,42 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.1.1",
+ "requestCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.3",
+ "requestCounter": 0
+ },
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.3.3",
+ "requestCounter": 0
+ }
+ ],
+ "4.4.4.4": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.4",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_topo1/r2/show_ipv4_route.ref b/tests/topotests/ldp_topo1/r2/show_ipv4_route.ref
new file mode 100644
index 0000000..90e1896
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_ipv4_route.ref
@@ -0,0 +1,7 @@
+O>* 1.1.1.1/32 [110/10] via 10.0.1.1, r2-eth0, label implicit-null, weight 1
+O 2.2.2.2/32 [110/0] is directly connected, lo, weight 1
+O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r2-eth1, label implicit-null, weight 1
+O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r2-eth1, label implicit-null, weight 1
+O 10.0.1.0/24 [110/10] is directly connected, r2-eth0, weight 1
+O 10.0.2.0/24 [110/10] is directly connected, r2-eth1, weight 1
+O 10.0.3.0/24 [110/10] is directly connected, r2-eth2, weight 1
diff --git a/tests/topotests/ldp_topo1/r2/show_mpls_ldp_binding.ref b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_binding.ref
new file mode 100644
index 0000000..d7df72e
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_binding.ref
@@ -0,0 +1,22 @@
+AF Destination Nexthop Local Label Remote Label In Use
+ipv4 1.1.1.1/32 1.1.1.1 xxx imp-null yes
+ipv4 1.1.1.1/32 3.3.3.3 xxx xxx no
+ipv4 1.1.1.1/32 4.4.4.4 xxx xxx no
+ipv4 2.2.2.2/32 1.1.1.1 imp-null xxx no
+ipv4 2.2.2.2/32 3.3.3.3 imp-null xxx no
+ipv4 2.2.2.2/32 4.4.4.4 imp-null xxx no
+ipv4 3.3.3.3/32 1.1.1.1 xxx xxx no
+ipv4 3.3.3.3/32 3.3.3.3 xxx imp-null yes
+ipv4 3.3.3.3/32 4.4.4.4 xxx xxx no
+ipv4 4.4.4.4/32 1.1.1.1 xxx xxx no
+ipv4 4.4.4.4/32 3.3.3.3 xxx xxx no
+ipv4 4.4.4.4/32 4.4.4.4 xxx imp-null yes
+ipv4 10.0.1.0/24 1.1.1.1 imp-null imp-null no
+ipv4 10.0.1.0/24 3.3.3.3 imp-null xxx no
+ipv4 10.0.1.0/24 4.4.4.4 imp-null xxx no
+ipv4 10.0.2.0/24 1.1.1.1 imp-null xxx no
+ipv4 10.0.2.0/24 3.3.3.3 imp-null imp-null no
+ipv4 10.0.2.0/24 4.4.4.4 imp-null imp-null no
+ipv4 10.0.3.0/24 1.1.1.1 imp-null xxx no
+ipv4 10.0.3.0/24 3.3.3.3 imp-null imp-null no
+ipv4 10.0.3.0/24 4.4.4.4 imp-null xxx no
diff --git a/tests/topotests/ldp_topo1/r2/show_mpls_ldp_discovery.ref b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_discovery.ref
new file mode 100644
index 0000000..6405b5e
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_discovery.ref
@@ -0,0 +1,4 @@
+AF ID Type Source Holdtime
+ipv4 1.1.1.1 Link r2-eth0 15
+ipv4 3.3.3.3 Link r2-eth1 15
+ipv4 4.4.4.4 Link r2-eth1 15
diff --git a/tests/topotests/ldp_topo1/r2/show_mpls_ldp_interface.ref b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_interface.ref
new file mode 100644
index 0000000..f9fc984
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_interface.ref
@@ -0,0 +1,3 @@
+AF Interface State Uptime Hello Timers ac
+ipv4 r2-eth0 ACTIVE xx:xx:xx 5/15 1
+ipv4 r2-eth1 ACTIVE xx:xx:xx 5/15 2
diff --git a/tests/topotests/ldp_topo1/r2/show_mpls_ldp_neighbor.ref b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_neighbor.ref
new file mode 100644
index 0000000..1172cbf
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_mpls_ldp_neighbor.ref
@@ -0,0 +1,4 @@
+AF ID State Remote Address Uptime
+ipv4 1.1.1.1 OPERATIONAL 1.1.1.1 xx:xx:xx
+ipv4 3.3.3.3 OPERATIONAL 3.3.3.3 xx:xx:xx
+ipv4 4.4.4.4 OPERATIONAL 4.4.4.4 xx:xx:xx
diff --git a/tests/topotests/ldp_topo1/r2/show_mpls_table.ref b/tests/topotests/ldp_topo1/r2/show_mpls_table.ref
new file mode 100644
index 0000000..df05a6b
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/show_mpls_table.ref
@@ -0,0 +1,7 @@
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------
+ XX LDP 10.0.1.1 implicit-null
+ XX LDP 10.0.2.3 implicit-null
+ XX LDP 10.0.2.4 implicit-null
+ XX LDP 10.0.3.3 implicit-null
+
diff --git a/tests/topotests/ldp_topo1/r2/zebra.conf b/tests/topotests/ldp_topo1/r2/zebra.conf
new file mode 100644
index 0000000..dd1dbac
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r2/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname r2
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to sw0
+ ip address 10.0.1.2/24
+ no link-detect
+!
+interface r2-eth1
+ description to sw1
+ ip address 10.0.2.2/24
+ no link-detect
+!
+interface r2-eth2
+ description to sw2
+ ip address 10.0.3.2/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r3/ip_mpls_route.ref b/tests/topotests/ldp_topo1/r3/ip_mpls_route.ref
new file mode 100644
index 0000000..21750b4
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/ip_mpls_route.ref
@@ -0,0 +1,4 @@
+xx proto xx nexthopvia inet 10.0.2.2 dev r3-eth0 nexthopvia inet 10.0.3.2 dev r3-eth1
+xx proto xx nexthopvia inet 10.0.2.2 dev r3-eth0 nexthopvia inet 10.0.3.2 dev r3-eth1
+xx proto xx nexthopvia inet 10.0.2.2 dev r3-eth0 nexthopvia inet 10.0.3.2 dev r3-eth1
+xx via inet 10.0.2.4 dev r3-eth0 proto xx
diff --git a/tests/topotests/ldp_topo1/r3/ldpd.conf b/tests/topotests/ldp_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..48956cb
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ !
+ interface r3-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r3/ospfd.conf b/tests/topotests/ldp_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..bd86fe4
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/ospfd.conf
@@ -0,0 +1,16 @@
+hostname r3
+password 1
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/0 area 0
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..d3c5024
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,32 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.2",
+ "requestCounter": 0
+ },
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.3.2",
+ "requestCounter": 0
+ }
+ ],
+ "4.4.4.4": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.4",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_topo1/r3/show_ipv4_route.ref b/tests/topotests/ldp_topo1/r3/show_ipv4_route.ref
new file mode 100644
index 0000000..9b9c763
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_ipv4_route.ref
@@ -0,0 +1,7 @@
+O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r3-eth0, label xxx, weight 1
+O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r3-eth0, label implicit-null, weight 1
+O 3.3.3.3/32 [110/0] is directly connected, lo, weight 1
+O>* 4.4.4.4/32 [110/10] via 10.0.2.4, r3-eth0, label implicit-null, weight 1
+O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r3-eth0, label implicit-null, weight 1
+O 10.0.2.0/24 [110/10] is directly connected, r3-eth0, weight 1
+O 10.0.3.0/24 [110/10] is directly connected, r3-eth1, weight 1
diff --git a/tests/topotests/ldp_topo1/r3/show_mpls_ldp_binding.ref b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_binding.ref
new file mode 100644
index 0000000..058a245
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_binding.ref
@@ -0,0 +1,15 @@
+AF Destination Nexthop Local Label Remote Label In Use
+ipv4 1.1.1.1/32 2.2.2.2 xxx xxx yes
+ipv4 1.1.1.1/32 4.4.4.4 xxx xxx no
+ipv4 2.2.2.2/32 2.2.2.2 xxx imp-null yes
+ipv4 2.2.2.2/32 4.4.4.4 xxx xxx no
+ipv4 3.3.3.3/32 2.2.2.2 imp-null xxx no
+ipv4 3.3.3.3/32 4.4.4.4 imp-null xxx no
+ipv4 4.4.4.4/32 2.2.2.2 xxx xxx no
+ipv4 4.4.4.4/32 4.4.4.4 xxx imp-null yes
+ipv4 10.0.1.0/24 2.2.2.2 xxx imp-null yes
+ipv4 10.0.1.0/24 4.4.4.4 xxx xxx no
+ipv4 10.0.2.0/24 2.2.2.2 imp-null imp-null no
+ipv4 10.0.2.0/24 4.4.4.4 imp-null imp-null no
+ipv4 10.0.3.0/24 2.2.2.2 imp-null imp-null no
+ipv4 10.0.3.0/24 4.4.4.4 imp-null xxx no
diff --git a/tests/topotests/ldp_topo1/r3/show_mpls_ldp_discovery.ref b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_discovery.ref
new file mode 100644
index 0000000..e3dbf06
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_discovery.ref
@@ -0,0 +1,3 @@
+AF ID Type Source Holdtime
+ipv4 2.2.2.2 Link r3-eth0 15
+ipv4 4.4.4.4 Link r3-eth0 15
diff --git a/tests/topotests/ldp_topo1/r3/show_mpls_ldp_interface.ref b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_interface.ref
new file mode 100644
index 0000000..243811e
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_interface.ref
@@ -0,0 +1,2 @@
+AF Interface State Uptime Hello Timers ac
+ipv4 r3-eth0 ACTIVE xx:xx:xx 5/15 2
diff --git a/tests/topotests/ldp_topo1/r3/show_mpls_ldp_neighbor.ref b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_neighbor.ref
new file mode 100644
index 0000000..769f782
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_mpls_ldp_neighbor.ref
@@ -0,0 +1,3 @@
+AF ID State Remote Address Uptime
+ipv4 2.2.2.2 OPERATIONAL 2.2.2.2 xx:xx:xx
+ipv4 4.4.4.4 OPERATIONAL 4.4.4.4 xx:xx:xx
diff --git a/tests/topotests/ldp_topo1/r3/show_mpls_table.ref b/tests/topotests/ldp_topo1/r3/show_mpls_table.ref
new file mode 100644
index 0000000..3978895
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/show_mpls_table.ref
@@ -0,0 +1,10 @@
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------
+ XX LDP 10.0.2.2 XX
+ XX LDP 10.0.2.2 implicit-null
+ XX LDP 10.0.2.2 implicit-null
+ XX LDP 10.0.2.4 implicit-null
+ XX LDP 10.0.3.2 XX
+ XX LDP 10.0.3.2 implicit-null
+ XX LDP 10.0.3.2 implicit-null
+
diff --git a/tests/topotests/ldp_topo1/r3/zebra.conf b/tests/topotests/ldp_topo1/r3/zebra.conf
new file mode 100644
index 0000000..456820f
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to sw1
+ ip address 10.0.2.3/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw2
+ ip address 10.0.3.3/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r4/ip_mpls_route.ref b/tests/topotests/ldp_topo1/r4/ip_mpls_route.ref
new file mode 100644
index 0000000..aef2fef
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/ip_mpls_route.ref
@@ -0,0 +1,5 @@
+xx proto xx nexthopvia inet 10.0.2.2 dev r4-eth0 nexthopvia inet 10.0.2.3 dev r4-eth0
+xx as to xx via inet 10.0.2.2 dev r4-eth0 proto xx
+xx via inet 10.0.2.2 dev r4-eth0 proto xx
+xx via inet 10.0.2.2 dev r4-eth0 proto xx
+xx via inet 10.0.2.3 dev r4-eth0 proto xx
diff --git a/tests/topotests/ldp_topo1/r4/ldpd.conf b/tests/topotests/ldp_topo1/r4/ldpd.conf
new file mode 100644
index 0000000..1d04aa0
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/ldpd.conf
@@ -0,0 +1,23 @@
+hostname r4
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 4.4.4.4
+ !
+ address-family ipv4
+ discovery transport-address 4.4.4.4
+ !
+ interface r4-eth0
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/r4/ospfd.conf b/tests/topotests/ldp_topo1/r4/ospfd.conf
new file mode 100644
index 0000000..5aae885
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/ospfd.conf
@@ -0,0 +1,11 @@
+hostname r4
+log file ospfd.log
+!
+router ospf
+ router-id 4.4.4.4
+ network 0.0.0.0/0 area 0
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..20751a2
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_ip_ospf_neighbor.json
@@ -0,0 +1,24 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.2",
+ "requestCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 1,
+ "converged": "Full",
+ "address": "10.0.2.3",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_topo1/r4/show_ipv4_route.ref b/tests/topotests/ldp_topo1/r4/show_ipv4_route.ref
new file mode 100644
index 0000000..7444cc9
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_ipv4_route.ref
@@ -0,0 +1,7 @@
+O>* 1.1.1.1/32 [110/20] via 10.0.2.2, r4-eth0, label xxx, weight 1
+O>* 2.2.2.2/32 [110/10] via 10.0.2.2, r4-eth0, label implicit-null, weight 1
+O>* 3.3.3.3/32 [110/10] via 10.0.2.3, r4-eth0, label implicit-null, weight 1
+O 4.4.4.4/32 [110/0] is directly connected, lo, weight 1
+O>* 10.0.1.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null, weight 1
+O 10.0.2.0/24 [110/10] is directly connected, r4-eth0, weight 1
+O>* 10.0.3.0/24 [110/20] via 10.0.2.2, r4-eth0, label implicit-null, weight 1
diff --git a/tests/topotests/ldp_topo1/r4/show_mpls_ldp_binding.ref b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_binding.ref
new file mode 100644
index 0000000..1e9dfa3
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_binding.ref
@@ -0,0 +1,15 @@
+AF Destination Nexthop Local Label Remote Label In Use
+ipv4 1.1.1.1/32 2.2.2.2 xxx xxx yes
+ipv4 1.1.1.1/32 3.3.3.3 xxx xxx no
+ipv4 2.2.2.2/32 2.2.2.2 xxx imp-null yes
+ipv4 2.2.2.2/32 3.3.3.3 xxx xxx no
+ipv4 3.3.3.3/32 2.2.2.2 xxx xxx no
+ipv4 3.3.3.3/32 3.3.3.3 xxx imp-null yes
+ipv4 4.4.4.4/32 2.2.2.2 imp-null xxx no
+ipv4 4.4.4.4/32 3.3.3.3 imp-null xxx no
+ipv4 10.0.1.0/24 2.2.2.2 xxx imp-null yes
+ipv4 10.0.1.0/24 3.3.3.3 xxx xxx no
+ipv4 10.0.2.0/24 2.2.2.2 imp-null imp-null no
+ipv4 10.0.2.0/24 3.3.3.3 imp-null imp-null no
+ipv4 10.0.3.0/24 2.2.2.2 xxx imp-null yes
+ipv4 10.0.3.0/24 3.3.3.3 xxx imp-null yes
diff --git a/tests/topotests/ldp_topo1/r4/show_mpls_ldp_discovery.ref b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_discovery.ref
new file mode 100644
index 0000000..a702657
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_discovery.ref
@@ -0,0 +1,3 @@
+AF ID Type Source Holdtime
+ipv4 2.2.2.2 Link r4-eth0 15
+ipv4 3.3.3.3 Link r4-eth0 15
diff --git a/tests/topotests/ldp_topo1/r4/show_mpls_ldp_interface.ref b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_interface.ref
new file mode 100644
index 0000000..dd57656
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_interface.ref
@@ -0,0 +1,2 @@
+AF Interface State Uptime Hello Timers ac
+ipv4 r4-eth0 ACTIVE xx:xx:xx 5/15 2
diff --git a/tests/topotests/ldp_topo1/r4/show_mpls_ldp_neighbor.ref b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_neighbor.ref
new file mode 100644
index 0000000..7c60522
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_mpls_ldp_neighbor.ref
@@ -0,0 +1,3 @@
+AF ID State Remote Address Uptime
+ipv4 2.2.2.2 OPERATIONAL 2.2.2.2 xx:xx:xx
+ipv4 3.3.3.3 OPERATIONAL 3.3.3.3 xx:xx:xx
diff --git a/tests/topotests/ldp_topo1/r4/show_mpls_table.ref b/tests/topotests/ldp_topo1/r4/show_mpls_table.ref
new file mode 100644
index 0000000..174dceb
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/show_mpls_table.ref
@@ -0,0 +1,9 @@
+ Inbound Label Type Nexthop Outbound Label
+ -----------------------------------------------
+ XX LDP 10.0.2.2 XX
+ XX LDP 10.0.2.2 implicit-null
+ XX LDP 10.0.2.2 implicit-null
+ XX LDP 10.0.2.2 implicit-null
+ XX LDP 10.0.2.3 implicit-null
+ XX LDP 10.0.2.3 implicit-null
+
diff --git a/tests/topotests/ldp_topo1/r4/zebra.conf b/tests/topotests/ldp_topo1/r4/zebra.conf
new file mode 100644
index 0000000..4a270af
--- /dev/null
+++ b/tests/topotests/ldp_topo1/r4/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname r4
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface r4-eth0
+ description to sw1
+ ip address 10.0.2.4/24
+ no link-detect
+!
+ip forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ldp_topo1/test_ldp_topo1.py b/tests/topotests/ldp_topo1/test_ldp_topo1.py
new file mode 100644
index 0000000..cb8adfb
--- /dev/null
+++ b/tests/topotests/ldp_topo1/test_ldp_topo1.py
@@ -0,0 +1,857 @@
+#!/usr/bin/env python
+
+#
+# test_bgp_multiview_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 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.
+#
+
+r"""
+test_ldp_topo1.py: Simple FRR LDP Test
+
+ +---------+
+ | r1 |
+ | 1.1.1.1 |
+ +----+----+
+ | .1 r1-eth0
+ |
+ ~~~~~~~~~~~~~
+ ~~ sw0 ~~
+ ~~ 10.0.1.0/24 ~~
+ ~~~~~~~~~~~~~
+ |10.0.1.0/24
+ |
+ | .2 r2-eth0
+ +----+----+
+ | r2 |
+ | 2.2.2.2 |
+ +--+---+--+
+ r2-eth2 .2 | | .2 r2-eth1
+ ______/ \______
+ / \
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+~~ sw2 ~~ ~~ sw1 ~~
+~~ 10.0.3.0/24 ~~ ~~ 10.0.2.0/24 ~~
+ ~~~~~~~~~~~~~ ~~~~~~~~~~~~~
+ | / |
+ \ _________/ |
+ \ / \
+r3-eth1 .3 | | .3 r3-eth0 | .4 r4-eth0
+ +----+--+---+ +----+----+
+ | r3 | | r4 |
+ | 3.3.3.3 | | 4.4.4.4 |
+ +-----------+ +---------+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+from functools import partial
+from time import sleep
+from lib.topolog import logger
+
+# 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.dirname(os.path.dirname(os.path.abspath(__file__))))
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+fatal_error = ""
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.ospfd]
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+
+ # Setup Routers
+ for i in range(1, 5):
+ tgen.add_router("r%s" % i)
+
+ # First switch
+ switch = tgen.add_switch("sw0")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ # Second switch
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+ # Third switch
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+#####################################################
+##
+## Helper functions
+##
+#####################################################
+
+
+def router_compare_json_output(rname, command, reference, count=60, wait=1):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # 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, wait)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ print("\n\n** %s: Setup Topology" % module.__name__)
+ print("******************************************\n")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ net = tgen.net
+
+ # Starting Routers
+ for i in range(1, 5):
+ net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ospfd", "%s/r%s/ospfd.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ldpd", "%s/r%s/ldpd.conf" % (thisDir, i))
+ tgen.gears["r%s" % i].start()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(module):
+ print("\n\n** %s: Shutdown Topology" % module.__name__)
+ print("******************************************\n")
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_router_running():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check if FRR is running on each Router node")
+ print("******************************************\n")
+ sleep(5)
+
+ # Starting Routers
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_mpls_interfaces():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify MPLS Interfaces
+ print("\n\n** Verifying MPLS Interfaces")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_mpls_ldp_interface.ref"
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp interface" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Timer in Uptime
+ actual = re.sub(r" [0-9][0-9]:[0-9][0-9]:[0-9][0-9] ", " xx:xx:xx ", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS LDP interface status",
+ title2="expected MPLS LDP interface status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS LDP Interface status Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ if failures > 0:
+ fatal_error = "MPLS LDP Interface status failed"
+
+ assert (
+ failures == 0
+ ), "MPLS LDP Interface status failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_ospf_convergence():
+ logger.info("Test: check OSPF adjacencies")
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json"
+ )
+
+
+def test_mpls_ldp_neighbor_establish():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ neighbors_operational = {
+ 1: 1,
+ 2: 3,
+ 3: 2,
+ 4: 2,
+ }
+
+ # Wait for MPLS LDP neighbors to establish.
+ print("\n\n** Verify MPLS LDP neighbors to establish")
+ print("******************************************\n")
+ timeout = 90
+ while timeout > 0:
+ print("Timeout in %s: " % timeout),
+ sys.stdout.flush()
+ # Look for any node not yet converged
+ for i in range(1, 5):
+ established = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null')
+ .rstrip()
+ )
+
+ # On current version, we need to make sure they all turn to OPERATIONAL on all lines
+ #
+ lines = ("\n".join(established.splitlines()) + "\n").splitlines(1)
+ # Check all lines to be either table header (starting with ^AF or show OPERATIONAL)
+ header = r"^AF.*"
+ operational = r"^ip.*OPERATIONAL.*"
+ found_operational = 0
+ for j in range(1, len(lines)):
+ if (not re.search(header, lines[j])) and (
+ not re.search(operational, lines[j])
+ ):
+ established = "" # Empty string shows NOT established
+ if re.search(operational, lines[j]):
+ found_operational += 1
+
+ logger.info("Found operational %d" % found_operational)
+ if found_operational < 1:
+ # Need at least one operational neighbor
+ established = "" # Empty string shows NOT established
+ else:
+ if found_operational != neighbors_operational[i]:
+ established = ""
+ if not established:
+ print("Waiting for r%s" % i)
+ sys.stdout.flush()
+ break
+ if not established:
+ sleep(5)
+ timeout -= 5
+ else:
+ print("Done")
+ break
+ else:
+ # Bail out with error if a router fails to converge
+ fatal_error = "MPLS LDP neighbors did not establish"
+ assert False, "MPLS LDP neighbors did not establish"
+
+ print("MPLS LDP neighbors established.")
+
+ if timeout < 60:
+ # Only wait if we actually went through a convergence
+ print("\nwaiting 15s for LDP sessions to establish")
+ sleep(15)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_mpls_ldp_discovery():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify MPLS LDP discovery
+ print("\n\n** Verifying MPLS LDP discovery")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_mpls_ldp_discovery.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null')
+ .rstrip()
+ )
+
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp discovery" 2> /dev/null')
+ .rstrip()
+ )
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS LDP discovery output",
+ title2="expected MPLS LDP discovery output",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS LDP discovery output Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "MPLS LDP Interface discovery output for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_mpls_ldp_neighbor():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify MPLS LDP neighbor
+ print("\n\n** Verifying MPLS LDP neighbor")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_mpls_ldp_neighbor.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp neighbor" 2> /dev/null')
+ .rstrip()
+ )
+
+ # Mask out changing parts in output
+ # Mask out Timer in Uptime
+ actual = re.sub(
+ r"(ipv4 [0-9\.]+ +OPERATIONAL [0-9\.]+ +)[0-9][0-9]:[0-9][0-9]:[0-9][0-9]",
+ r"\1xx:xx:xx",
+ actual,
+ )
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS LDP neighbor output",
+ title2="expected MPLS LDP neighbor output",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS LDP neighbor output Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "MPLS LDP Interface neighbor output for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_mpls_ldp_binding():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip this test for now until proper sorting of the output
+ # is implemented
+ # pytest.skip("Skipping test_mpls_ldp_binding")
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify MPLS LDP binding
+ print("\n\n** Verifying MPLS LDP binding")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_mpls_ldp_binding.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show mpls ldp binding" 2> /dev/null')
+ .rstrip()
+ )
+
+ # Mask out changing parts in output
+ # Mask out label
+ actual = re.sub(
+ r"(ipv4 [0-9\./]+ +[0-9\.]+ +)[0-9][0-9] (.*)", r"\1xxx\2", actual
+ )
+ actual = re.sub(
+ r"(ipv4 [0-9\./]+ +[0-9\.]+ +[a-z\-]+ +)[0-9][0-9] (.*)",
+ r"\1xxx\2",
+ actual,
+ )
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Sort lines which start with "xx via inet "
+ pattern = r"^\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+"
+ swapped = True
+ while swapped:
+ swapped = False
+ for j in range(1, len(actual)):
+ if re.search(pattern, actual[j]) and re.search(
+ pattern, actual[j - 1]
+ ):
+ if actual[j - 1] > actual[j]:
+ temp = actual[j - 1]
+ actual[j - 1] = actual[j]
+ actual[j] = temp
+ swapped = True
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS LDP binding output",
+ title2="expected MPLS LDP binding output",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS LDP binding output Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "MPLS LDP binding output for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_zebra_ipv4_routingTable():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify Zebra IPv4 Routing Table
+ print("\n\n** Verifying Zebra IPv4 Routing Table")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_ipv4_route.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^O"')
+ .rstrip()
+ )
+ # Drop timers on end of line
+ actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual)
+
+ # Mask out label - all LDP labels should be >= 10 (2-digit)
+ # leaving the implicit labels unmasked
+ actual = re.sub(r" label [0-9][0-9]+", " label xxx", actual)
+ # and translating remaining implicit (single-digit) labels to label implicit-null
+ actual = re.sub(r" label [0-9]+", " label implicit-null", actual)
+ # Check if we have implicit labels - if not, then remove them from reference
+ if not re.search(r" label implicit-null", actual):
+ expected = re.sub(r", label implicit-null", "", expected)
+
+ # now fix newlines of expected (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual IPv4 zebra routing table",
+ title2="expected IPv4 zebra routing table",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed IPv4 Zebra Routing Table Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "IPv4 Zebra Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_mpls_table():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify MPLS table
+ print("\n\n** Verifying MPLS table")
+ print("******************************************\n")
+ failures = 0
+
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/show_mpls_table.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = net["r%s" % i].cmd('vtysh -c "show mpls table" 2> /dev/null')
+
+ # Fix inconsistent Label numbers at beginning of line
+ actual = re.sub(r"(\s+)[0-9]+(\s+LDP)", r"\1XX\2", actual)
+ # Fix inconsistent Label numbers at end of line
+ actual = re.sub(
+ r"(\s+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\s+)[0-9][0-9]", r"\1XX", actual
+ )
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Sort lines which start with " XX LDP"
+ pattern = r"^\s+[0-9X]+\s+LDP"
+ swapped = True
+ while swapped:
+ swapped = False
+ for j in range(1, len(actual)):
+ if re.search(pattern, actual[j]) and re.search(
+ pattern, actual[j - 1]
+ ):
+ if actual[j - 1] > actual[j]:
+ temp = actual[j - 1]
+ actual[j - 1] = actual[j]
+ actual[j] = temp
+ swapped = True
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual MPLS table output",
+ title2="expected MPLS table output",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed MPLS table output Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "MPLS table output for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_linux_mpls_routes():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify Linux Kernel MPLS routes
+ print("\n\n** Verifying Linux Kernel MPLS routes")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 5):
+ refTableFile = "%s/r%s/ip_mpls_route.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i].cmd("ip -o -family mpls route 2> /dev/null").rstrip()
+ )
+
+ # Mask out label and protocol
+ actual = re.sub(r"[0-9][0-9] via inet ", "xx via inet ", actual)
+ actual = re.sub(r"[0-9][0-9] +proto", "xx proto", actual)
+ actual = re.sub(r"[0-9][0-9] as to ", "xx as to ", actual)
+ actual = re.sub(r"[ ]+proto \w+", " proto xx", actual)
+
+ # Sort nexthops
+ nexthop_sorted = []
+ for line in actual.splitlines():
+ tokens = re.split(r"\\\t", line.strip())
+ nexthop_sorted.append(
+ "{} {}".format(
+ tokens[0].strip(),
+ " ".join([token.strip() for token in sorted(tokens[1:])]),
+ ).strip()
+ )
+
+ # Sort lines and fixup differences between old and new iproute
+ actual = "\n".join(sorted(nexthop_sorted))
+ actual = re.sub(r"nexthop via", "nexthopvia", actual)
+ actual = re.sub(r" nexthop as to xx via inet ", " nexthopvia inet ", actual)
+ actual = re.sub(r" weight 1", "", actual)
+ actual = re.sub(r" [ ]+", " ", actual)
+
+ # put \n back at line ends
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual Linux Kernel MPLS route",
+ title2="expected Linux Kernel MPLS route",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed Linux Kernel MPLS route output Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "Linux Kernel MPLS route output for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are running
+ for i in range(1, 5):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_shutdown_check_stderr():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ print(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying unexpected STDERR output from daemons")
+ print("******************************************\n")
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ log = net["r%s" % i].getStdErr("ldpd")
+ if log:
+ print("\nRouter r%s LDPd StdErr Log:\n%s" % (i, log))
+ log = net["r%s" % i].getStdErr("ospfd")
+ if log:
+ print("\nRouter r%s OSPFd StdErr Log:\n%s" % (i, log))
+ log = net["r%s" % i].getStdErr("zebra")
+ if log:
+ print("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
+
+
+def test_shutdown_check_memleak():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
+ print(
+ "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
+ )
+ pytest.skip("Skipping test for memory leaks")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ net["r%s" % i].report_memory_leaks(
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
+ )
+
+
+if __name__ == "__main__":
+
+ # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/ldp_vpls_topo1/__init__.py b/tests/topotests/ldp_vpls_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/__init__.py
diff --git a/tests/topotests/ldp_vpls_topo1/ce1/zebra.conf b/tests/topotests/ldp_vpls_topo1/ce1/zebra.conf
new file mode 100644
index 0000000..6f165e2
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/ce1/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce1
+!
+interface ce1-eth0
+ ip address 172.16.1.1/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/ce2/zebra.conf b/tests/topotests/ldp_vpls_topo1/ce2/zebra.conf
new file mode 100644
index 0000000..ac02d0f
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/ce2/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce2
+!
+interface ce2-eth0
+ ip address 172.16.1.2/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/ce3/zebra.conf b/tests/topotests/ldp_vpls_topo1/ce3/zebra.conf
new file mode 100644
index 0000000..c6a5824
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/ce3/zebra.conf
@@ -0,0 +1,12 @@
+log file zebra.log
+!
+hostname ce3
+!
+interface ce3-eth0
+ ip address 172.16.1.3/24
+ no link-detect
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r1/ldpd.conf b/tests/topotests/ldp_vpls_topo1/r1/ldpd.conf
new file mode 100644
index 0000000..a19e5cc
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r1
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 1.1.1.1
+ !
+ address-family ipv4
+ discovery transport-address 1.1.1.1
+ ttl-security disable
+ label local allocate host-routes
+ !
+ interface r1-eth1
+ !
+ interface r1-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r1-eth0
+ !
+ member pseudowire r1-mpw0
+ neighbor lsr-id 2.2.2.2
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt b/tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/ospf-nbrs.txt
diff --git a/tests/topotests/ldp_vpls_topo1/r1/ospfd.conf b/tests/topotests/ldp_vpls_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..a66fb92
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r1
+log file ospfd.log
+!
+router ospf
+ router-id 1.1.1.1
+ network 0.0.0.0/0 area 0
+!
+int r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
+int r1-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..90c8195
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "2.2.2.2": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 2,
+ "converged": "Full",
+ "address": "10.0.1.2",
+ "ifaceName": "r1-eth1:10.0.1.1",
+ "requestCounter": 0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "dbSummaryCounter": 0,
+ "retransmitCounter": 0,
+ "priority": 2,
+ "converged": "Full",
+ "address": "10.0.2.3",
+ "ifaceName": "r1-eth2:10.0.2.1",
+ "requestCounter": 0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_route.ref b/tests/topotests/ldp_vpls_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..fdb81f2
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ip_route_after_link_down.ref b/tests/topotests/ldp_vpls_topo1/r1/show_ip_route_after_link_down.ref
new file mode 100644
index 0000000..84113a0
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ip_route_after_link_down.ref
@@ -0,0 +1,20 @@
+{
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_binding.ref b/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_binding.ref
new file mode 100644
index 0000000..b3de7e2
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "2.2.2.2: 100":{
+ "destination":"2.2.2.2",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref b/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref
new file mode 100644
index 0000000..29e9df1
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r1-mpw0":{
+ "peerId":"2.2.2.2",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ldp_binding.ref b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_binding.ref
new file mode 100644
index 0000000..b3a12ec
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ldp_discovery.ref b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_discovery.ref
new file mode 100644
index 0000000..9301e60
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r1-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"targeted",
+ "peer":"2.2.2.2",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r1-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/show_ldp_neighbor.ref b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_neighbor.ref
new file mode 100644
index 0000000..40d8ebe
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r1/zebra.conf b/tests/topotests/ldp_vpls_topo1/r1/zebra.conf
new file mode 100644
index 0000000..bbb98d2
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r1/zebra.conf
@@ -0,0 +1,29 @@
+log file zebra.log
+!
+hostname r1
+!
+! debug zebra kernel
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra nht
+! debug zebra pseudowires
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface r1-eth0
+ description to s1
+!
+interface r1-eth1
+ description to s4
+ ip address 10.0.1.1/24
+!
+interface r1-eth2
+ description to s5
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r2/ldpd.conf b/tests/topotests/ldp_vpls_topo1/r2/ldpd.conf
new file mode 100644
index 0000000..447b3f1
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/ldpd.conf
@@ -0,0 +1,35 @@
+hostname r2
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 2.2.2.2
+ !
+ address-family ipv4
+ discovery transport-address 2.2.2.2
+ ttl-security disable
+ label local allocate host-routes
+ !
+ interface r2-eth1
+ !
+ interface r2-eth2
+ !
+ !
+!
+l2vpn CUST_A type vpls
+ member interface r2-eth0
+ !
+ member pseudowire r2-mpw0
+ neighbor lsr-id 1.1.1.1
+ pw-id 100
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r2/ospfd.conf b/tests/topotests/ldp_vpls_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..b4692fe
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r2
+log file ospfd.log
+!
+router ospf
+ router-id 2.2.2.2
+ network 0.0.0.0/0 area 0
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
+int r2-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 1
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..29dde53
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.1.1",
+ "ifaceName":"r2-eth1:10.0.1.2",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "3.3.3.3": [
+ {
+ "priority":2,
+ "converged":"Full",
+ "address":"10.0.3.3",
+ "ifaceName":"r2-eth2:10.0.3.2",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ip_route.ref b/tests/topotests/ldp_vpls_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..6056fef
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_binding.ref b/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_binding.ref
new file mode 100644
index 0000000..42c5a1c
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_binding.ref
@@ -0,0 +1,16 @@
+{
+ "1.1.1.1: 100":{
+ "destination":"1.1.1.1",
+ "vcId":100,
+ "localLabel":16,
+ "localControlWord":1,
+ "localVcType":"Ethernet",
+ "localGroupID":0,
+ "localIfMtu":1500,
+ "remoteLabel":16,
+ "remoteControlWord":1,
+ "remoteVcType":"Ethernet",
+ "remoteGroupID":0,
+ "remoteIfMtu":1500
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref b/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref
new file mode 100644
index 0000000..942ed23
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_l2vpn_vc.ref
@@ -0,0 +1,8 @@
+{
+ "r2-mpw0":{
+ "peerId":"1.1.1.1",
+ "vcId":100,
+ "VpnName":"CUST_A",
+ "status":"up"
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ldp_binding.ref b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_binding.ref
new file mode 100644
index 0000000..c641fb4
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"3.3.3.3",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"3.3.3.3",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"3.3.3.3",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ldp_discovery.ref b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_discovery.ref
new file mode 100644
index 0000000..26801ac
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_discovery.ref
@@ -0,0 +1,25 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r2-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"targeted",
+ "peer":"1.1.1.1",
+ "helloHoldtime":45
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "type":"link",
+ "interface":"r2-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/show_ldp_neighbor.ref b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_neighbor.ref
new file mode 100644
index 0000000..eed3528
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"3.3.3.3",
+ "state":"OPERATIONAL",
+ "transportAddress":"3.3.3.3"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r2/zebra.conf b/tests/topotests/ldp_vpls_topo1/r2/zebra.conf
new file mode 100644
index 0000000..c79b210
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r2/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r2
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface r2-eth0
+ description to s2
+!
+interface r2-eth1
+ description to s4
+ ip address 10.0.1.2/24
+!
+interface r2-eth2
+ description to s6
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r3/ldpd.conf b/tests/topotests/ldp_vpls_topo1/r3/ldpd.conf
new file mode 100644
index 0000000..ab51471
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/ldpd.conf
@@ -0,0 +1,27 @@
+hostname r3
+log file ldpd.log
+!
+! debug mpls ldp zebra
+! debug mpls ldp event
+! debug mpls ldp errors
+! debug mpls ldp messages recv
+! debug mpls ldp messages sent
+! debug mpls ldp discovery hello recv
+! debug mpls ldp discovery hello sent
+!
+mpls ldp
+ router-id 3.3.3.3
+ !
+ address-family ipv4
+ discovery transport-address 3.3.3.3
+ ttl-security disable
+ label local allocate host-routes
+ !
+ interface r3-eth1
+ !
+ interface r3-eth2
+ !
+ !
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r3/ospfd.conf b/tests/topotests/ldp_vpls_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..2413bfa
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/ospfd.conf
@@ -0,0 +1,17 @@
+hostname r3
+log file ospfd.log
+!
+router ospf
+ router-id 3.3.3.3
+ network 0.0.0.0/0 area 0
+!
+int r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
+int r3-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 2
+!
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..9966297
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_ospf_neighbor.json
@@ -0,0 +1,26 @@
+{
+ "neighbors": {
+ "1.1.1.1": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.2.1",
+ "ifaceName":"r3-eth1:10.0.2.3",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "2.2.2.2": [
+ {
+ "priority":1,
+ "converged":"Full",
+ "address":"10.0.3.2",
+ "ifaceName":"r3-eth2:10.0.3.3",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ip_route.ref b/tests/topotests/ldp_vpls_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..fc96ada
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ip_route.ref
@@ -0,0 +1,147 @@
+{
+ "1.1.1.1/32":[
+ {
+ "prefix":"1.1.1.1/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2/32":[
+ {
+ "prefix":"2.2.2.2/32",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3/32":[
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"3.3.3.3/32",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "protocol":"ospf",
+ "selected":true,
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.1",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0/24":[
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0/24":[
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.3.0/24",
+ "protocol":"connected",
+ "selected":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r3-eth2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_binding.ref b/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_binding.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_binding.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref b/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_l2vpn_vc.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ldp_binding.ref b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_binding.ref
new file mode 100644
index 0000000..e54bd6e
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_binding.ref
@@ -0,0 +1,44 @@
+{
+ "bindings":[
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"1.1.1.1",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"1.1.1.1/32",
+ "neighborId":"2.2.2.2",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"1.1.1.1",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"2.2.2.2/32",
+ "neighborId":"2.2.2.2",
+ "remoteLabel":"imp-null",
+ "inUse":1
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"1.1.1.1",
+ "localLabel":"imp-null",
+ "inUse":0
+ },
+ {
+ "addressFamily":"ipv4",
+ "prefix":"3.3.3.3/32",
+ "neighborId":"2.2.2.2",
+ "localLabel":"imp-null",
+ "inUse":0
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ldp_discovery.ref b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_discovery.ref
new file mode 100644
index 0000000..42fa98d
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_discovery.ref
@@ -0,0 +1,18 @@
+{
+ "adjacencies":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "type":"link",
+ "interface":"r3-eth1",
+ "helloHoldtime":15
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "type":"link",
+ "interface":"r3-eth2",
+ "helloHoldtime":15
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/show_ldp_neighbor.ref b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_neighbor.ref
new file mode 100644
index 0000000..5c482da
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/show_ldp_neighbor.ref
@@ -0,0 +1,16 @@
+{
+ "neighbors":[
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"1.1.1.1",
+ "state":"OPERATIONAL",
+ "transportAddress":"1.1.1.1"
+ },
+ {
+ "addressFamily":"ipv4",
+ "neighborId":"2.2.2.2",
+ "state":"OPERATIONAL",
+ "transportAddress":"2.2.2.2"
+ }
+ ]
+}
diff --git a/tests/topotests/ldp_vpls_topo1/r3/zebra.conf b/tests/topotests/ldp_vpls_topo1/r3/zebra.conf
new file mode 100644
index 0000000..bcc0d8d
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/r3/zebra.conf
@@ -0,0 +1,28 @@
+log file zebra.log
+!
+hostname r3
+!
+! debug zebra rib detailed
+! debug zebra dplane detailed
+! debug zebra kernel
+! debug zebra nht
+! debug zebra pseudowires
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface r3-eth0
+ description to s3
+!
+interface r3-eth1
+ description to s5
+ ip address 10.0.2.3/24
+!
+interface r3-eth2
+ description to s6
+ ip address 10.0.3.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.dot b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.dot
new file mode 100644
index 0000000..4f1bd22
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.dot
@@ -0,0 +1,111 @@
+## 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="Test Topology - LDP-VPLS 1";
+
+ # Routers
+ ce1 [
+ shape=doubleoctagon,
+ label="ce1",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce2 [
+ shape=doubleoctagon
+ label="ce2",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ ce3 [
+ shape=doubleoctagon
+ label="ce3",
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ 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,
+ ];
+
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="VPLS\n172.16.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="s4\n10.0.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ shape=oval,
+ label="s5\n10.0.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ shape=oval,
+ label="s6\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ ce1 -- s1 [label="eth0\n.1"];
+ ce2 -- s2 [label="eth0\n.2"];
+ ce3 -- s3 [label="eth0\n.3"];
+
+ r1 -- s1 [label="eth0"];
+ r1 -- s4 [label="eth1\n.1"];
+ r1 -- s5 [label="eth2\n.1"];
+
+ r2 -- s2 [label="eth0"];
+ r2 -- s4 [label="eth1\n.2"];
+ r2 -- s6 [label="eth2\n.2"];
+
+ r3 -- s3 [label="eth0"];
+ r3 -- s5 [label="eth1\n.3"];
+ r3 -- s6 [label="eth2\n.3"];
+}
diff --git a/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.pdf b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.pdf
new file mode 100644
index 0000000..4d26732
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.pdf
Binary files differ
diff --git a/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py
new file mode 100644
index 0000000..8a41ea5
--- /dev/null
+++ b/tests/topotests/ldp_vpls_topo1/test_ldp_vpls_topo1.py
@@ -0,0 +1,304 @@
+#!/usr/bin/env python
+
+#
+# test_ldp_vpls_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_ldp_vpls_topo1.py:
+
+ +---------+ +---------+
+ | | | |
+ | CE1 | | CE2 |
+ | | | |
+ +---------+ +---------+
+ce1-eth0 (172.16.1.1/24)| |ce2-eth0 (172.16.1.2/24)
+ | |
+ | |
+ rt1-eth0| |rt2-eth0
+ +---------+ 10.0.1.0/24 +---------+
+ | |rt1-eth1 | |
+ | RT1 +----------------+ RT2 |
+ | 1.1.1.1 | rt2-eth1| 2.2.2.2 |
+ | | | |
+ +---------+ +---------+
+ rt1-eth2| |rt2-eth2
+ | |
+ | |
+ 10.0.2.0/24| +---------+ |10.0.3.0/24
+ | | | |
+ | | RT3 | |
+ +--------+ 3.3.3.3 +-------+
+ rt3-eth2| |rt3-eth1
+ +---------+
+ |rt3-eth0
+ |
+ |
+ ce3-eth0 (172.16.1.3/24)|
+ +---------+
+ | |
+ | CE3 |
+ | |
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.ldpd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["ce1", "ce2", "ce3", "r1", "r2", "r3"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["ce1"])
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["ce2"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["ce3"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ # Don't start ospfd and ldpd in the CE nodes
+ if router.name[0] == "r":
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_LDP, os.path.join(CWD, "{}/ldpd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference, count=80, wait=1):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # 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, wait)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_convergence():
+ logger.info("Test: check OSPF adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json"
+ )
+
+
+def test_rib():
+ logger.info("Test: verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(rname, "show ip route json", "show_ip_route.ref")
+
+
+def test_ldp_adjacencies():
+ logger.info("Test: verify LDP adjacencies")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp discovery json", "show_ldp_discovery.ref"
+ )
+
+
+def test_ldp_neighbors():
+ logger.info("Test: verify LDP neighbors")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp neighbor json", "show_ldp_neighbor.ref"
+ )
+
+
+def test_ldp_bindings():
+ logger.info("Test: verify LDP bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show mpls ldp binding json", "show_ldp_binding.ref"
+ )
+
+
+def test_ldp_pwid_bindings():
+ logger.info("Test: verify LDP PW-ID bindings")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom binding json", "show_l2vpn_binding.ref"
+ )
+
+
+def test_ldp_pseudowires():
+ logger.info("Test: verify LDP pseudowires")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["r1", "r2", "r3"]:
+ router_compare_json_output(
+ rname, "show l2vpn atom vc json", "show_l2vpn_vc.ref"
+ )
+
+
+def test_ldp_pseudowires_after_link_down():
+ logger.info("Test: verify LDP pseudowires after r1-r2 link goes down")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Shut down r1-r2 link */
+ tgen = get_topogen()
+ rname = "r1"
+ tgen.gears[rname].peer_link_enable("r1-eth1", False)
+ router_compare_json_output(
+ rname,
+ "show ip route json",
+ "show_ip_route_after_link_down.ref",
+ count=160,
+ wait=1,
+ )
+ # 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", count=160, wait=1
+ )
+
+
+# Memory leak test template
+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/lib/__init__.py b/tests/topotests/lib/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/lib/__init__.py
diff --git a/tests/topotests/lib/bgp.py b/tests/topotests/lib/bgp.py
new file mode 100644
index 0000000..7ab36c4
--- /dev/null
+++ b/tests/topotests/lib/bgp.py
@@ -0,0 +1,5564 @@
+#
+# Copyright (c) 2019 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.
+#
+
+import ipaddress
+import sys
+import traceback
+from copy import deepcopy
+from time import sleep
+
+# Import common_config to use commomnly used APIs
+from lib.common_config import (
+ create_common_configurations,
+ FRRCFG_FILE,
+ InvalidCLIError,
+ apply_raw_config,
+ check_address_types,
+ find_interface_with_greater_ip,
+ generate_ips,
+ get_frr_ipv6_linklocal,
+ retry,
+ run_frr_cmd,
+ validate_ip_address,
+)
+from lib.topogen import get_topogen
+from lib.topolog import logger
+from lib.topotest import frr_unicode
+
+from lib import topotest
+
+
+def create_router_bgp(tgen, topo=None, input_dict=None, build=False, load_config=True):
+ """
+ API to configure bgp on router
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "local_as": "200",
+ "router_id": "22.22.22.22",
+ "graceful-restart": {
+ "graceful-restart": True,
+ "preserve-fw-state": True,
+ "timer": {
+ "restart-time": 30,
+ "rib-stale-time": 30,
+ "select-defer-time": 30,
+ }
+ },
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "default_originate":{
+ "neighbor":"R2",
+ "add_type":"lo"
+ "route_map":"rm"
+
+ },
+ "redistribute": [{
+ "redist_type": "static",
+ "attribute": {
+ "metric" : 123
+ }
+ },
+ {"redist_type": "connected"}
+ ],
+ "advertise_networks": [
+ {
+ "network": "20.0.0.0/32",
+ "no_of_network": 10
+ },
+ {
+ "network": "30.0.0.0/32",
+ "no_of_network": 10
+ }
+ ],
+ "neighbor": {
+ "r3": {
+ "keepalivetimer": 60,
+ "holddowntimer": 180,
+ "dest_link": {
+ "r4": {
+ "allowas-in": {
+ "number_occurences": 2
+ },
+ "prefix_lists": [
+ {
+ "name": "pf_list_1",
+ "direction": "in"
+ }
+ ],
+ "route_maps": [{
+ "name": "RMAP_MED_R3",
+ "direction": "in"
+ }],
+ "next_hop_self": True
+ },
+ "r1": {"graceful-restart-helper": True}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ # Flag is used when testing ipv6 over ipv4 or vice-versa
+ afi_test = False
+
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ topo = topo["routers"]
+ input_dict = deepcopy(input_dict)
+
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ if "bgp" not in input_dict[router]:
+ logger.debug("Router %s: 'bgp' not present in input_dict", router)
+ continue
+
+ bgp_data_list = input_dict[router]["bgp"]
+
+ if type(bgp_data_list) is not list:
+ bgp_data_list = [bgp_data_list]
+
+ config_data = []
+
+ for bgp_data in bgp_data_list:
+ data_all_bgp = __create_bgp_global(tgen, bgp_data, router, build)
+ if data_all_bgp:
+ bgp_addr_data = bgp_data.setdefault("address_family", {})
+
+ if not bgp_addr_data:
+ logger.debug(
+ "Router %s: 'address_family' not present in "
+ "input_dict for BGP",
+ router,
+ )
+ else:
+
+ ipv4_data = bgp_addr_data.setdefault("ipv4", {})
+ ipv6_data = bgp_addr_data.setdefault("ipv6", {})
+ l2vpn_data = bgp_addr_data.setdefault("l2vpn", {})
+
+ neigh_unicast = (
+ True
+ if ipv4_data.setdefault("unicast", {})
+ or ipv6_data.setdefault("unicast", {})
+ else False
+ )
+
+ l2vpn_evpn = True if l2vpn_data.setdefault("evpn", {}) else False
+
+ if neigh_unicast:
+ data_all_bgp = __create_bgp_unicast_neighbor(
+ tgen,
+ topo,
+ bgp_data,
+ router,
+ afi_test,
+ 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
+ )
+ if data_all_bgp:
+ config_data.extend(data_all_bgp)
+
+ if config_data:
+ config_data_dict[router] = config_data
+
+ try:
+ result = create_common_configurations(
+ tgen, config_data_dict, "bgp", build, load_config
+ )
+ except InvalidCLIError:
+ logger.error("create_router_bgp", exc_info=True)
+ result = False
+
+ logger.debug("Exiting lib API: create_router_bgp()")
+ return result
+
+
+def __create_bgp_global(tgen, input_dict, router, build=False):
+ """
+ Helper API to create bgp global configuration.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `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.
+
+ Returns
+ -------
+ list of config commands
+ """
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ bgp_data = input_dict
+ del_bgp_action = bgp_data.setdefault("delete", False)
+
+ config_data = []
+
+ if "local_as" not in bgp_data and build:
+ logger.debug(
+ "Router %s: 'local_as' not present in input_dict" "for BGP", router
+ )
+ return config_data
+
+ local_as = bgp_data.setdefault("local_as", "")
+ cmd = "router bgp {}".format(local_as)
+ vrf_id = bgp_data.setdefault("vrf", None)
+ if vrf_id:
+ cmd = "{} vrf {}".format(cmd, vrf_id)
+
+ if del_bgp_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ return config_data
+
+ config_data.append(cmd)
+ config_data.append("no bgp ebgp-requires-policy")
+
+ router_id = bgp_data.setdefault("router_id", None)
+ del_router_id = bgp_data.setdefault("del_router_id", False)
+ if del_router_id:
+ config_data.append("no bgp router-id")
+ if router_id:
+ config_data.append("bgp router-id {}".format(router_id))
+
+ config_data.append("bgp log-neighbor-changes")
+ config_data.append("no bgp network import-check")
+ bgp_peer_grp_data = bgp_data.setdefault("peer-group", {})
+
+ if "peer-group" in bgp_data and bgp_peer_grp_data:
+ peer_grp_data = __create_bgp_peer_group(tgen, bgp_peer_grp_data, router)
+ config_data.extend(peer_grp_data)
+
+ bst_path = bgp_data.setdefault("bestpath", None)
+ if bst_path:
+ if "aspath" in bst_path:
+ if "delete" in bst_path:
+ config_data.append(
+ "no bgp bestpath as-path {}".format(bst_path["aspath"])
+ )
+ else:
+ config_data.append("bgp bestpath as-path {}".format(bst_path["aspath"]))
+
+ if "graceful-restart" in bgp_data:
+ graceful_config = bgp_data["graceful-restart"]
+
+ graceful_restart = graceful_config.setdefault("graceful-restart", None)
+
+ graceful_restart_disable = graceful_config.setdefault(
+ "graceful-restart-disable", None
+ )
+
+ preserve_fw_state = graceful_config.setdefault("preserve-fw-state", None)
+
+ disable_eor = graceful_config.setdefault("disable-eor", None)
+
+ if graceful_restart == False:
+ cmd = "no bgp graceful-restart"
+ if graceful_restart:
+ cmd = "bgp graceful-restart"
+
+ if graceful_restart is not None:
+ config_data.append(cmd)
+
+ if graceful_restart_disable == False:
+ cmd = "no bgp graceful-restart-disable"
+ if graceful_restart_disable:
+ cmd = "bgp graceful-restart-disable"
+
+ if graceful_restart_disable is not None:
+ config_data.append(cmd)
+
+ if preserve_fw_state == False:
+ cmd = "no bgp graceful-restart preserve-fw-state"
+ if preserve_fw_state:
+ cmd = "bgp graceful-restart preserve-fw-state"
+
+ if preserve_fw_state is not None:
+ config_data.append(cmd)
+
+ if disable_eor == False:
+ cmd = "no bgp graceful-restart disable-eor"
+ if disable_eor:
+ cmd = "bgp graceful-restart disable-eor"
+
+ if disable_eor is not None:
+ config_data.append(cmd)
+
+ if "timer" in bgp_data["graceful-restart"]:
+ timer = bgp_data["graceful-restart"]["timer"]
+
+ if "delete" in timer:
+ del_action = timer["delete"]
+ else:
+ del_action = False
+
+ for rs_timer, value in timer.items():
+ rs_timer_value = timer.setdefault(rs_timer, None)
+
+ if rs_timer_value and rs_timer != "delete":
+ cmd = "bgp graceful-restart {} {}".format(rs_timer, rs_timer_value)
+
+ if del_action:
+ 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_unicast_neighbor(
+ tgen, topo, input_dict, router, afi_test, config_data=None
+):
+ """
+ Helper API to create configuration for address-family unicast
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured.
+ * `afi_test` : use when ipv6 needs to be tested over ipv4 or vice-versa
+ * `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))
+
+ add_neigh = True
+ bgp_data = input_dict
+ if "router bgp" in config_data:
+ add_neigh = False
+
+ bgp_data = input_dict["address_family"]
+
+ for addr_type, addr_dict in bgp_data.items():
+ if not addr_dict:
+ continue
+
+ if not check_address_types(addr_type) and not afi_test:
+ continue
+
+ addr_data = addr_dict["unicast"]
+ if addr_data:
+ config_data.append("address-family {} unicast".format(addr_type))
+
+ advertise_network = addr_data.setdefault("advertise_networks", [])
+ for advertise_network_dict in advertise_network:
+ network = advertise_network_dict["network"]
+ if type(network) is not list:
+ network = [network]
+
+ if "no_of_network" in advertise_network_dict:
+ no_of_network = advertise_network_dict["no_of_network"]
+ else:
+ no_of_network = 1
+
+ del_action = advertise_network_dict.setdefault("delete", False)
+
+ # Generating IPs for verification
+ network_list = generate_ips(network, no_of_network)
+ for ip in network_list:
+ ip = str(ipaddress.ip_network(frr_unicode(ip)))
+
+ cmd = "network {}".format(ip)
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ import_cmd = addr_data.setdefault("import", {})
+ if import_cmd:
+ try:
+ if import_cmd["delete"]:
+ config_data.append("no import vrf {}".format(import_cmd["vrf"]))
+ except KeyError:
+ config_data.append("import vrf {}".format(import_cmd["vrf"]))
+
+ max_paths = addr_data.setdefault("maximum_paths", {})
+ if max_paths:
+ ibgp = max_paths.setdefault("ibgp", None)
+ ebgp = max_paths.setdefault("ebgp", None)
+ del_cmd = max_paths.setdefault("delete", False)
+ if ibgp:
+ if del_cmd:
+ config_data.append("no maximum-paths ibgp {}".format(ibgp))
+ else:
+ config_data.append("maximum-paths ibgp {}".format(ibgp))
+ if ebgp:
+ if del_cmd:
+ config_data.append("no maximum-paths {}".format(ebgp))
+ else:
+ config_data.append("maximum-paths {}".format(ebgp))
+
+ aggregate_addresses = addr_data.setdefault("aggregate_address", [])
+ for aggregate_address in aggregate_addresses:
+ network = aggregate_address.setdefault("network", None)
+ if not network:
+ logger.debug(
+ "Router %s: 'network' not present in " "input_dict for BGP", router
+ )
+ else:
+ cmd = "aggregate-address {}".format(network)
+
+ as_set = aggregate_address.setdefault("as_set", False)
+ summary = aggregate_address.setdefault("summary", False)
+ del_action = aggregate_address.setdefault("delete", False)
+ if as_set:
+ cmd = "{} as-set".format(cmd)
+ if summary:
+ cmd = "{} summary".format(cmd)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ redistribute_data = addr_data.setdefault("redistribute", {})
+ if redistribute_data:
+ for redistribute in redistribute_data:
+ if "redist_type" not in redistribute:
+ logger.debug(
+ "Router %s: 'redist_type' not present in " "input_dict", router
+ )
+ else:
+ cmd = "redistribute {}".format(redistribute["redist_type"])
+ redist_attr = redistribute.setdefault("attribute", None)
+ if redist_attr:
+ if type(redist_attr) is dict:
+ for key, value in redist_attr.items():
+ cmd = "{} {} {}".format(cmd, key, value)
+ else:
+ cmd = "{} {}".format(cmd, redist_attr)
+
+ del_action = redistribute.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ admin_dist_data = addr_data.setdefault("distance", {})
+ if admin_dist_data:
+ if len(admin_dist_data) < 2:
+ logger.debug(
+ "Router %s: pass the admin distance values for "
+ "ebgp, ibgp and local routes",
+ router,
+ )
+ cmd = "distance bgp {} {} {}".format(
+ admin_dist_data["ebgp"],
+ admin_dist_data["ibgp"],
+ admin_dist_data["local"],
+ )
+
+ del_action = admin_dist_data.setdefault("delete", False)
+ if del_action:
+ cmd = "no distance bgp"
+ config_data.append(cmd)
+
+ import_vrf_data = addr_data.setdefault("import", {})
+ if import_vrf_data:
+ cmd = "import vrf {}".format(import_vrf_data["vrf"])
+
+ del_action = import_vrf_data.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if "neighbor" in addr_data:
+ neigh_data = __create_bgp_neighbor(
+ topo, input_dict, router, addr_type, add_neigh
+ )
+ config_data.extend(neigh_data)
+ # configure default originate
+ if "default_originate" in addr_data:
+ default_originate_config = __create_bgp_default_originate_neighbor(
+ topo, input_dict, router, addr_type, add_neigh
+ )
+ config_data.extend(default_originate_config)
+
+ for addr_type, addr_dict in bgp_data.items():
+ if not addr_dict or not check_address_types(addr_type):
+ continue
+
+ addr_data = addr_dict["unicast"]
+ if "neighbor" in addr_data:
+ neigh_addr_data = __create_bgp_unicast_address_family(
+ topo, input_dict, router, addr_type, add_neigh
+ )
+
+ config_data.extend(neigh_addr_data)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return config_data
+
+
+def __create_bgp_default_originate_neighbor(
+ topo, input_dict, router, addr_type, add_neigh=True
+):
+ """
+ Helper API to create neighbor default - originate configuration
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured
+ """
+ tgen = get_topogen()
+ config_data = []
+ logger.debug("Entering lib API: __create_bgp_default_originate_neighbor()")
+
+ bgp_data = input_dict["address_family"]
+ neigh_data = bgp_data[addr_type]["unicast"]["default_originate"]
+ for name, peer_dict in neigh_data.items():
+ nh_details = topo[name]
+
+ neighbor_ip = None
+ if "dest-link" in neigh_data[name]:
+ dest_link = neigh_data[name]["dest-link"]
+ neighbor_ip = nh_details["links"][dest_link][addr_type].split("/")[0]
+ elif "add_type" in neigh_data[name]:
+ add_type = neigh_data[name]["add_type"]
+ neighbor_ip = nh_details["links"][add_type][addr_type].split("/")[0]
+ else:
+ neighbor_ip = nh_details["links"][router][addr_type].split("/")[0]
+
+ config_data.append("address-family {} unicast".format(addr_type))
+ if "route_map" in peer_dict:
+ route_map = peer_dict["route_map"]
+ if "delete" in peer_dict:
+ if peer_dict["delete"]:
+ config_data.append(
+ "no neighbor {} default-originate route-map {}".format(
+ neighbor_ip, route_map
+ )
+ )
+ else:
+ config_data.append(
+ " neighbor {} default-originate route-map {}".format(
+ neighbor_ip, route_map
+ )
+ )
+ else:
+ config_data.append(
+ " neighbor {} default-originate route-map {}".format(
+ neighbor_ip, route_map
+ )
+ )
+
+ else:
+ if "delete" in peer_dict:
+ if peer_dict["delete"]:
+ config_data.append(
+ "no neighbor {} default-originate".format(neighbor_ip)
+ )
+ else:
+ config_data.append(
+ "neighbor {} default-originate".format(neighbor_ip)
+ )
+ else:
+ config_data.append("neighbor {} default-originate".format(neighbor_ip))
+
+ logger.debug("Exiting lib API: __create_bgp_default_originate_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.items():
+ 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 type(unicast_type) is 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 type(action) is 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_peer_group(topo, input_dict, router):
+ """
+ Helper API to create neighbor specific configuration
+
+ Parameters
+ ----------
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured
+ """
+ config_data = []
+ logger.debug("Entering lib API: __create_bgp_peer_group()")
+
+ for grp, grp_dict in input_dict.items():
+ config_data.append("neighbor {} peer-group".format(grp))
+ neigh_cxt = "neighbor {} ".format(grp)
+ update_source = grp_dict.setdefault("update-source", None)
+ remote_as = grp_dict.setdefault("remote-as", None)
+ capability = grp_dict.setdefault("capability", None)
+ if update_source:
+ config_data.append("{} update-source {}".format(neigh_cxt, update_source))
+
+ if remote_as:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
+
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
+
+ logger.debug("Exiting lib API: __create_bgp_peer_group()")
+ return config_data
+
+
+def __create_bgp_neighbor(topo, input_dict, router, addr_type, add_neigh=True):
+ """
+ Helper API to create neighbor specific configuration
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router id to be configured
+ """
+ config_data = []
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+ bgp_data = input_dict["address_family"]
+ neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
+ global_connect = input_dict.get("connecttimer", 5)
+
+ for name, peer_dict in neigh_data.items():
+ for dest_link, peer in peer_dict["dest_link"].items():
+ nh_details = topo[name]
+
+ if "vrfs" in topo[router] or type(nh_details["bgp"]) is list:
+ for vrf_data in nh_details["bgp"]:
+ if "vrf" in nh_details["links"][dest_link] and "vrf" in vrf_data:
+ if nh_details["links"][dest_link]["vrf"] == vrf_data["vrf"]:
+ remote_as = vrf_data["local_as"]
+ break
+ else:
+ if "vrf" not in vrf_data:
+ remote_as = vrf_data["local_as"]
+ break
+
+ else:
+ remote_as = nh_details["bgp"]["local_as"]
+
+ update_source = None
+
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[0]
+ except KeyError:
+ intf = topo[name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, name, intf)
+ if "delete" in peer and peer["delete"]:
+ neigh_cxt = "no neighbor {}".format(ip_addr)
+ config_data.append("{}".format(neigh_cxt))
+ return config_data
+ else:
+ neigh_cxt = "neighbor {}".format(ip_addr)
+
+ if "peer-group" in peer:
+ config_data.append(
+ "neighbor {} interface peer-group {}".format(
+ ip_addr, peer["peer-group"]
+ )
+ )
+
+ # Loopback interface
+ if "source_link" in peer:
+ if peer["source_link"] == "lo":
+ update_source = topo[router]["links"]["lo"][addr_type].split("/")[0]
+ else:
+ update_source = topo[router]["links"][peer["source_link"]][
+ "interface"
+ ]
+ if "peer-group" not in peer:
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ config_data.append(
+ "{} interface remote-as {}".format(neigh_cxt, remote_as)
+ )
+ elif add_neigh:
+ config_data.append("{} remote-as {}".format(neigh_cxt, remote_as))
+
+ if addr_type == "ipv6":
+ config_data.append("address-family ipv6 unicast")
+ config_data.append("{} activate".format(neigh_cxt))
+
+ if "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ config_data.append(
+ "{} update-source {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+ config_data.append(
+ "{} interface {}".format(
+ neigh_cxt, nh_details["links"][dest_link]["peer-interface"]
+ )
+ )
+
+ disable_connected = peer.setdefault("disable_connected_check", False)
+ connect = peer.get("connecttimer", global_connect)
+ keep_alive = peer.setdefault("keepalivetimer", 3)
+ hold_down = peer.setdefault("holddowntimer", 10)
+ password = peer.setdefault("password", None)
+ no_password = peer.setdefault("no_password", None)
+ capability = peer.setdefault("capability", None)
+ max_hop_limit = peer.setdefault("ebgp_multihop", 1)
+
+ graceful_restart = peer.setdefault("graceful-restart", None)
+ graceful_restart_helper = peer.setdefault("graceful-restart-helper", None)
+ graceful_restart_disable = peer.setdefault("graceful-restart-disable", None)
+ if capability:
+ config_data.append("{} capability {}".format(neigh_cxt, capability))
+
+ if update_source:
+ config_data.append(
+ "{} update-source {}".format(neigh_cxt, update_source)
+ )
+ if disable_connected:
+ config_data.append(
+ "{} disable-connected-check".format(disable_connected)
+ )
+ if update_source:
+ config_data.append(
+ "{} update-source {}".format(neigh_cxt, update_source)
+ )
+ if int(keep_alive) != 60 and int(hold_down) != 180:
+ config_data.append(
+ "{} timers {} {}".format(neigh_cxt, keep_alive, hold_down)
+ )
+ if int(connect) != 120:
+ config_data.append("{} timers connect {}".format(neigh_cxt, connect))
+
+ if graceful_restart:
+ config_data.append("{} graceful-restart".format(neigh_cxt))
+ elif graceful_restart == False:
+ config_data.append("no {} graceful-restart".format(neigh_cxt))
+
+ if graceful_restart_helper:
+ config_data.append("{} graceful-restart-helper".format(neigh_cxt))
+ elif graceful_restart_helper == False:
+ config_data.append("no {} graceful-restart-helper".format(neigh_cxt))
+
+ if graceful_restart_disable:
+ config_data.append("{} graceful-restart-disable".format(neigh_cxt))
+ elif graceful_restart_disable == False:
+ config_data.append("no {} graceful-restart-disable".format(neigh_cxt))
+
+ if password:
+ config_data.append("{} password {}".format(neigh_cxt, password))
+
+ if no_password:
+ config_data.append("no {} password {}".format(neigh_cxt, no_password))
+
+ if max_hop_limit > 1:
+ config_data.append(
+ "{} ebgp-multihop {}".format(neigh_cxt, max_hop_limit)
+ )
+ config_data.append("{} enforce-multihop".format(neigh_cxt))
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return config_data
+
+
+def __create_bgp_unicast_address_family(
+ topo, input_dict, router, addr_type, add_neigh=True
+):
+ """
+ API prints bgp global config to bgp_json file.
+
+ Parameters
+ ----------
+ * `bgp_cfg` : BGP class variables have BGP config saved in it for
+ particular router,
+ * `local_as_no` : Local as number
+ * `router_id` : Router-id
+ * `ecmp_path` : ECMP max path
+ * `gr_enable` : BGP global gracefull restart config
+ """
+
+ config_data = []
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+ bgp_data = input_dict["address_family"]
+ neigh_data = bgp_data[addr_type]["unicast"]["neighbor"]
+
+ for peer_name, peer_dict in deepcopy(neigh_data).items():
+ for dest_link, peer in peer_dict["dest_link"].items():
+ deactivate = None
+ activate = None
+ nh_details = topo[peer_name]
+ activate_addr_family = peer.setdefault("activate", None)
+ deactivate_addr_family = peer.setdefault("deactivate", None)
+ # Loopback interface
+ if "source_link" in peer and peer["source_link"] == "lo":
+ for destRouterLink, data in sorted(nh_details["links"].items()):
+ if "type" in data and data["type"] == "loopback":
+ if dest_link == destRouterLink:
+ ip_addr = (
+ nh_details["links"][destRouterLink][addr_type]
+ .split("/")[0]
+ .lower()
+ )
+
+ # Physical interface
+ else:
+ # check the neighbor type if un numbered nbr, use interface.
+ if "neighbor_type" in peer and peer["neighbor_type"] == "unnumbered":
+ ip_addr = nh_details["links"][dest_link]["peer-interface"]
+ elif "neighbor_type" in peer and peer["neighbor_type"] == "link-local":
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ elif dest_link in nh_details["links"].keys():
+ try:
+ ip_addr = nh_details["links"][dest_link][addr_type].split("/")[
+ 0
+ ]
+ except KeyError:
+ intf = topo[peer_name]["links"][dest_link]["interface"]
+ ip_addr = get_frr_ipv6_linklocal(tgen, peer_name, intf)
+ if (
+ addr_type == "ipv4"
+ and bgp_data["ipv6"]
+ and check_address_types("ipv6")
+ and "ipv6" in nh_details["links"][dest_link]
+ ):
+ deactivate = nh_details["links"][dest_link]["ipv6"].split("/")[
+ 0
+ ]
+
+ neigh_cxt = "neighbor {}".format(ip_addr)
+ config_data.append("address-family {} unicast".format(addr_type))
+
+ if activate_addr_family is not None:
+ config_data.append(
+ "address-family {} unicast".format(activate_addr_family)
+ )
+
+ config_data.append("{} activate".format(neigh_cxt))
+
+ if deactivate and activate_addr_family is None:
+ config_data.append("no neighbor {} activate".format(deactivate))
+
+ if deactivate_addr_family is not None:
+ config_data.append(
+ "address-family {} unicast".format(deactivate_addr_family)
+ )
+ config_data.append("no {} activate".format(neigh_cxt))
+
+ next_hop_self = peer.setdefault("next_hop_self", None)
+ send_community = peer.setdefault("send_community", None)
+ prefix_lists = peer.setdefault("prefix_lists", {})
+ route_maps = peer.setdefault("route_maps", {})
+ no_send_community = peer.setdefault("no_send_community", None)
+ capability = peer.setdefault("capability", None)
+ allowas_in = peer.setdefault("allowas-in", None)
+
+ # next-hop-self
+ if next_hop_self is not None:
+ if next_hop_self is True:
+ config_data.append("{} next-hop-self".format(neigh_cxt))
+ else:
+ config_data.append("no {} next-hop-self".format(neigh_cxt))
+
+ # send_community
+ if send_community:
+ config_data.append("{} send-community".format(neigh_cxt))
+
+ # no_send_community
+ if no_send_community:
+ config_data.append(
+ "no {} send-community {}".format(neigh_cxt, no_send_community)
+ )
+
+ # capability_ext_nh
+ if capability and addr_type == "ipv6":
+ config_data.append("address-family ipv4 unicast")
+ config_data.append("{} activate".format(neigh_cxt))
+
+ if "allowas_in" in peer:
+ allow_as_in = peer["allowas_in"]
+ config_data.append("{} allowas-in {}".format(neigh_cxt, allow_as_in))
+
+ if "no_allowas_in" in peer:
+ allow_as_in = peer["no_allowas_in"]
+ config_data.append("no {} allowas-in {}".format(neigh_cxt, allow_as_in))
+
+ if "shutdown" in peer:
+ config_data.append(
+ "{} {} shutdown".format(
+ "no" if not peer["shutdown"] else "", neigh_cxt
+ )
+ )
+
+ if prefix_lists:
+ for prefix_list in prefix_lists:
+ name = prefix_list.setdefault("name", {})
+ direction = prefix_list.setdefault("direction", "in")
+ del_action = prefix_list.setdefault("delete", False)
+ if not name:
+ logger.info(
+ "Router %s: 'name' not present in "
+ "input_dict for BGP neighbor prefix lists",
+ router,
+ )
+ else:
+ cmd = "{} prefix-list {} {}".format(neigh_cxt, name, direction)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ 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 = "{} route-map {} {}".format(neigh_cxt, name, direction)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if allowas_in:
+ number_occurences = allowas_in.setdefault("number_occurences", {})
+ del_action = allowas_in.setdefault("delete", False)
+
+ cmd = "{} allowas-in {}".format(neigh_cxt, number_occurences)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ return config_data
+
+
+def modify_bgp_config_when_bgpd_down(tgen, topo, input_dict):
+ """
+ API will save the current config to router's /etc/frr/ for BGPd
+ daemon(bgpd.conf file)
+
+ Paramters
+ ---------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : defines for which router, and which config
+ needs to be modified
+
+ Usage:
+ ------
+ # Modify graceful-restart config not to set f-bit
+ # and write to /etc/frr
+
+ # Api call to delete advertised networks
+ input_dict_2 = {
+ "r5": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "101.0.20.1/32",
+ "no_of_network": 5,
+ "delete": True
+ }
+ ],
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "5::1/128",
+ "no_of_network": 5,
+ "delete": True
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = modify_bgp_config_when_bgpd_down(tgen, topo, input_dict)
+
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ try:
+ result = create_router_bgp(
+ tgen, topo, input_dict, build=False, load_config=False
+ )
+ if result is not True:
+ return result
+
+ # Copy bgp config file to /etc/frr
+ for dut in input_dict.keys():
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Delete BGP config when BGPd is down in {}".format(router))
+ # Reading the config from "rundir" and copy to /etc/frr/bgpd.conf
+ cmd = "cat {}/{}/{} >> /etc/frr/bgpd.conf".format(
+ tgen.logdir, router, FRRCFG_FILE
+ )
+ router_list[router].run(cmd)
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+#############################################
+# Verification APIs
+#############################################
+@retry(retry_timeout=8)
+def verify_router_id(tgen, topo, input_dict, expected=True):
+ """
+ Running command "show ip bgp json" for DUT and reading router-id
+ from input_dict and verifying with command output.
+ 1. Statically modfified router-id should take place
+ 2. When static router-id is deleted highest loopback should
+ become router-id
+ 3. When loopback intf is down then highest physcial intf
+ should become router-id
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `input_dict`: input dictionary, have details of Device Under Test, for
+ which user wants to test the data
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ # Verify if router-id for r1 is 12.12.12.12
+ input_dict = {
+ "r1":{
+ "router_id": "12.12.12.12"
+ }
+ # Verify that router-id for r1 is highest interface ip
+ input_dict = {
+ "routers": ["r1"]
+ }
+ result = verify_router_id(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():
+ if router not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[router]
+
+ del_router_id = input_dict[router]["bgp"].setdefault("del_router_id", False)
+
+ logger.info("Checking router %s router-id", router)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True)
+ router_id_out = show_bgp_json["ipv4Unicast"]["routerId"]
+ router_id_out = ipaddress.IPv4Address(frr_unicode(router_id_out))
+
+ # Once router-id is deleted, highest interface ip should become
+ # router-id
+ if del_router_id:
+ router_id = find_interface_with_greater_ip(topo, router)
+ else:
+ router_id = input_dict[router]["bgp"]["router_id"]
+ router_id = ipaddress.IPv4Address(frr_unicode(router_id))
+
+ if router_id == router_id_out:
+ logger.info("Found expected router-id %s for router %s", router_id, router)
+ else:
+ errormsg = (
+ "Router-id for router:{} mismatch, expected:"
+ " {} but found:{}".format(router, router_id, router_id_out)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=150)
+def verify_bgp_convergence(tgen, topo=None, dut=None, expected=True):
+ """
+ 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
+ """
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+ for router, rnode in tgen.routers().items():
+ if "bgp" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying BGP Convergence on router %s:", router)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True)
+ # Verifying output dictionary show_bgp_json is empty or not
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ bgp_data_list = topo["routers"][router]["bgp"]
+
+ if type(bgp_data_list) is not list:
+ bgp_data_list = [bgp_data_list]
+
+ for bgp_data in bgp_data_list:
+ if "vrf" in bgp_data:
+ vrf = bgp_data["vrf"]
+ if vrf is None:
+ vrf = "default"
+ else:
+ vrf = "default"
+
+ # To find neighbor ip type
+ bgp_addr_type = bgp_data["address_family"]
+ if "l2vpn" in bgp_addr_type:
+ total_evpn_peer = 0
+
+ if "neighbor" not in bgp_addr_type["l2vpn"]["evpn"]:
+ continue
+
+ bgp_neighbors = bgp_addr_type["l2vpn"]["evpn"]["neighbor"]
+ total_evpn_peer += len(bgp_neighbors)
+
+ no_of_evpn_peer = 0
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for _addr_type, dest_link_dict in peer_data.items():
+ data = topo["routers"][bgp_neighbor]["links"]
+ for dest_link in dest_link_dict.keys():
+ if dest_link in data:
+ peer_details = peer_data[_addr_type][dest_link]
+
+ neighbor_ip = data[dest_link][_addr_type].split("/")[0]
+ nh_state = None
+
+ if (
+ "ipv4Unicast" in show_bgp_json[vrf]
+ or "ipv6Unicast" in show_bgp_json[vrf]
+ ):
+ errormsg = (
+ "[DUT: %s] VRF: %s, "
+ "ipv4Unicast/ipv6Unicast"
+ " address-family present"
+ " under l2vpn" % (router, vrf)
+ )
+ return errormsg
+
+ l2VpnEvpn_data = show_bgp_json[vrf]["l2VpnEvpn"][
+ "peers"
+ ]
+ nh_state = l2VpnEvpn_data[neighbor_ip]["state"]
+
+ if nh_state == "Established":
+ no_of_evpn_peer += 1
+
+ if no_of_evpn_peer == total_evpn_peer:
+ logger.info(
+ "[DUT: %s] VRF: %s, BGP is Converged for " "epvn peers",
+ router,
+ vrf,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: %s] VRF: %s, BGP is not converged "
+ "for evpn peers" % (router, vrf)
+ )
+ return errormsg
+ else:
+ total_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor in bgp_neighbors:
+ total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
+
+ no_of_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link in peer_data["dest_link"].keys():
+ data = topo["routers"][bgp_neighbor]["links"]
+ if dest_link in data:
+ peer_details = peer_data["dest_link"][dest_link]
+ # for link local neighbors
+ if (
+ "neighbor_type" in peer_details
+ and peer_details["neighbor_type"] == "link-local"
+ ):
+ intf = topo["routers"][bgp_neighbor]["links"][
+ dest_link
+ ]["interface"]
+ neighbor_ip = get_frr_ipv6_linklocal(
+ tgen, bgp_neighbor, intf
+ )
+ elif "source_link" in peer_details:
+ neighbor_ip = topo["routers"][bgp_neighbor][
+ "links"
+ ][peer_details["source_link"]][addr_type].split(
+ "/"
+ )[
+ 0
+ ]
+ elif (
+ "neighbor_type" in peer_details
+ and peer_details["neighbor_type"] == "unnumbered"
+ ):
+ neighbor_ip = data[dest_link]["peer-interface"]
+ else:
+ neighbor_ip = data[dest_link][addr_type].split("/")[
+ 0
+ ]
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json[vrf]["ipv4Unicast"][
+ "peers"
+ ]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json[vrf]["ipv6Unicast"][
+ "peers"
+ ]
+ if neighbor_ip in ipv6_data:
+ nh_state = ipv6_data[neighbor_ip]["state"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer and no_of_peer > 0:
+ logger.info("[DUT: %s] VRF: %s, BGP is Converged", router, vrf)
+ result = True
+ else:
+ errormsg = "[DUT: %s] VRF: %s, BGP is not converged" % (router, vrf)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=16)
+def verify_bgp_community(
+ tgen,
+ addr_type,
+ router,
+ network,
+ input_dict=None,
+ vrf=None,
+ bestpath=False,
+ expected=True,
+):
+ """
+ API to veiryf BGP large community is attached in route for any given
+ DUT by running "show bgp ipv4/6 {route address} json" command.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test
+ * `network`: network for which set criteria needs to be verified
+ * `input_dict`: having details like - for which router, community and
+ values needs to be verified
+ * `vrf`: VRF name
+ * `bestpath`: To check best path cli
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ networks = ["200.50.2.0/32"]
+ input_dict = {
+ "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"
+ }
+ result = verify_bgp_community(tgen, "ipv4", dut, network, input_dict=None)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: verify_bgp_community()")
+ if router not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[router]
+
+ logger.info(
+ "Verifying BGP community attributes on dut %s: for %s " "network %s",
+ router,
+ addr_type,
+ network,
+ )
+
+ command = "show bgp"
+
+ for net in network:
+ if vrf:
+ cmd = "{} vrf {} {} {} json".format(command, vrf, addr_type, net)
+ elif bestpath:
+ cmd = "{} {} {} bestpath json".format(command, addr_type, net)
+ else:
+ cmd = "{} {} {} json".format(command, addr_type, net)
+
+ show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True)
+ if "paths" not in show_bgp_json:
+ return "Prefix {} not found in BGP table of router: {}".format(net, router)
+
+ as_paths = show_bgp_json["paths"]
+ found = False
+ for i in range(len(as_paths)):
+ if (
+ "largeCommunity" in show_bgp_json["paths"][i]
+ or "community" in show_bgp_json["paths"][i]
+ ):
+ found = True
+ logger.info(
+ "Large Community attribute is found for route:" " %s in router: %s",
+ net,
+ router,
+ )
+ if input_dict is not None:
+ for criteria, comm_val in input_dict.items():
+ show_val = show_bgp_json["paths"][i][criteria]["string"]
+ if comm_val == show_val:
+ logger.info(
+ "Verifying BGP %s for prefix: %s"
+ " in router: %s, found expected"
+ " value: %s",
+ criteria,
+ net,
+ router,
+ comm_val,
+ )
+ else:
+ errormsg = (
+ "Failed: Verifying BGP attribute"
+ " {} for route: {} in router: {}"
+ ", expected value: {} but found"
+ ": {}".format(criteria, net, router, comm_val, show_val)
+ )
+ return errormsg
+
+ if not found:
+ errormsg = (
+ "Large Community attribute is not found for route: "
+ "{} in router: {} ".format(net, router)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: verify_bgp_community()")
+ return True
+
+
+def modify_as_number(tgen, topo, input_dict):
+ """
+ API reads local_as and remote_as from user defined input_dict and
+ modify router"s ASNs accordingly. Router"s config is modified and
+ recent/changed config is loadeded to router.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : defines for which router ASNs needs to be modified
+
+ Usage
+ -----
+ To modify ASNs for router r1
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "local_as": 131079
+ }
+ }
+ result = modify_as_number(tgen, topo, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ try:
+
+ new_topo = deepcopy(topo["routers"])
+ router_dict = {}
+ for router in input_dict.keys():
+ # Remove bgp configuration
+
+ router_dict.update({router: {"bgp": {"delete": True}}})
+ try:
+ new_topo[router]["bgp"]["local_as"] = input_dict[router]["bgp"][
+ "local_as"
+ ]
+ except TypeError:
+ new_topo[router]["bgp"][0]["local_as"] = input_dict[router]["bgp"][
+ "local_as"
+ ]
+ logger.info("Removing bgp configuration")
+ create_router_bgp(tgen, topo, router_dict)
+
+ logger.info("Applying modified bgp configuration")
+ result = create_router_bgp(tgen, new_topo)
+ if result is not True:
+ result = "Error applying new AS number config"
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=8)
+def verify_as_numbers(tgen, topo, input_dict, expected=True):
+ """
+ This API is to verify AS numbers for given DUT by running
+ "show ip bgp neighbor json" command. Local AS and Remote AS
+ will ve verified with input_dict data and command output.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type, ipv4/ipv6
+ * `input_dict`: defines - for which router, AS numbers needs to be verified
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "local_as": 131079
+ }
+ }
+ }
+ result = verify_as_numbers(tgen, topo, addr_type, 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():
+ if router not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[router]
+
+ logger.info("Verifying AS numbers for dut %s:", router)
+
+ show_ip_bgp_neighbor_json = run_frr_cmd(
+ rnode, "show ip bgp neighbor json", isjson=True
+ )
+ local_as = input_dict[router]["bgp"]["local_as"]
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+
+ for addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ remote_as = input_dict[bgp_neighbor]["bgp"]["local_as"]
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ neighbor_ip = None
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+ neigh_data = show_ip_bgp_neighbor_json[neighbor_ip]
+ # Verify Local AS for router
+ if neigh_data["localAs"] != local_as:
+ errormsg = (
+ "Failed: Verify local_as for dut {},"
+ " found: {} but expected: {}".format(
+ router, neigh_data["localAs"], local_as
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "Verified local_as for dut %s, found" " expected: %s",
+ router,
+ local_as,
+ )
+
+ # Verify Remote AS for neighbor
+ if neigh_data["remoteAs"] != remote_as:
+ errormsg = (
+ "Failed: Verify remote_as for dut "
+ "{}'s neighbor {}, found: {} but "
+ "expected: {}".format(
+ router, bgp_neighbor, neigh_data["remoteAs"], remote_as
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "Verified remote_as for dut %s's "
+ "neighbor %s, found expected: %s",
+ router,
+ bgp_neighbor,
+ remote_as,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=150)
+def verify_bgp_convergence_from_running_config(tgen, dut=None, expected=True):
+ """
+ API to verify BGP convergence b/w loopback and physical interface.
+ This API would be used when routers have BGP neighborship is loopback
+ to physical or vice-versa
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ results = verify_bgp_convergence_bw_lo_and_phy_intf(tgen, topo,
+ dut="r1")
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying BGP Convergence on router %s:", router)
+ show_bgp_json = run_frr_cmd(rnode, "show bgp vrf all summary json", isjson=True)
+ # Verifying output dictionary show_bgp_json is empty or not
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ for vrf, addr_family_data in show_bgp_json.items():
+ for address_family, neighborship_data in addr_family_data.items():
+ total_peer = 0
+ no_of_peer = 0
+
+ total_peer = len(neighborship_data["peers"].keys())
+
+ for peer, peer_data in neighborship_data["peers"].items():
+ if peer_data["state"] == "Established":
+ no_of_peer += 1
+
+ if total_peer != no_of_peer:
+ errormsg = (
+ "[DUT: %s] VRF: %s, BGP is not converged"
+ " for peer: %s" % (router, vrf, peer)
+ )
+ return errormsg
+
+ logger.info("[DUT: %s]: vrf: %s, BGP is Converged", router, vrf)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_bgp(tgen, addr_type, router, vrf=None, neighbor=None):
+ """
+ This API is to clear bgp neighborship by running
+ clear ip bgp */clear bgp ipv6 * command,
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `addr_type`: ip type ipv4/ipv6
+ * `router`: device under test
+ * `vrf`: vrf name
+ * `neighbor`: Neighbor for which bgp needs to be cleared
+
+ Usage
+ -----
+ clear_bgp(tgen, addr_type, "r1")
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if router not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[router]
+
+ if vrf:
+ if type(vrf) is not list:
+ vrf = [vrf]
+
+ # Clearing BGP
+ logger.info("Clearing BGP neighborship for router %s..", router)
+ if addr_type == "ipv4":
+ if vrf:
+ for _vrf in vrf:
+ run_frr_cmd(rnode, "clear ip bgp vrf {} *".format(_vrf))
+ elif neighbor:
+ run_frr_cmd(rnode, "clear bgp ipv4 {}".format(neighbor))
+ else:
+ run_frr_cmd(rnode, "clear ip bgp *")
+ elif addr_type == "ipv6":
+ if vrf:
+ for _vrf in vrf:
+ run_frr_cmd(rnode, "clear bgp vrf {} ipv6 *".format(_vrf))
+ elif neighbor:
+ run_frr_cmd(rnode, "clear bgp ipv6 {}".format(neighbor))
+ else:
+ run_frr_cmd(rnode, "clear bgp ipv6 *")
+ else:
+ run_frr_cmd(rnode, "clear bgp *")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+def clear_bgp_and_verify(tgen, topo, router):
+ """
+ This API is to clear bgp neighborship and verify bgp neighborship
+ is coming up(BGP is converged) usinf "show bgp summary json" command
+ and also verifying for all bgp neighbors uptime before and after
+ clear bgp sessions is different as the uptime must be changed once
+ bgp sessions are cleared using "clear ip bgp */clear bgp ipv6 *" cmd.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `router`: device under test
+
+ Usage
+ -----
+ result = clear_bgp_and_verify(tgen, topo, addr_type, dut)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if router not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[router]
+
+ peer_uptime_before_clear_bgp = {}
+ sleeptime = 3
+
+ # Verifying BGP convergence before bgp clear command
+ for retry in range(50):
+ # Waiting for BGP to converge
+ logger.info(
+ "Waiting for %s sec for BGP to converge on router" " %s...",
+ sleeptime,
+ router,
+ )
+ sleep(sleeptime)
+
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True)
+ # Verifying output dictionary show_bgp_json is empty or not
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+ total_peer = 0
+ for addr_type in bgp_addr_type.keys():
+
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor in bgp_neighbors:
+ total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
+
+ no_of_peer = 0
+ for addr_type in bgp_addr_type:
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+
+ # Peer up time dictionary
+ peer_uptime_before_clear_bgp[bgp_neighbor] = ipv4_data[
+ neighbor_ip
+ ]["peerUptimeEstablishedEpoch"]
+ else:
+ ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
+ nh_state = ipv6_data[neighbor_ip]["state"]
+
+ # Peer up time dictionary
+ peer_uptime_before_clear_bgp[bgp_neighbor] = ipv6_data[
+ neighbor_ip
+ ]["peerUptimeEstablishedEpoch"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer:
+ logger.info("BGP is Converged for router %s before bgp" " clear", router)
+ break
+ else:
+ logger.info(
+ "BGP is not yet Converged for router %s " "before bgp clear", router
+ )
+ else:
+ errormsg = (
+ "TIMEOUT!! BGP is not converged in {} seconds for"
+ " router {}".format(retry * sleeptime, router)
+ )
+ return errormsg
+
+ # Clearing BGP
+ logger.info("Clearing BGP neighborship for router %s..", router)
+ for addr_type in bgp_addr_type.keys():
+ if addr_type == "ipv4":
+ run_frr_cmd(rnode, "clear ip bgp *")
+ elif addr_type == "ipv6":
+ run_frr_cmd(rnode, "clear bgp ipv6 *")
+
+ peer_uptime_after_clear_bgp = {}
+ # Verifying BGP convergence after bgp clear command
+ for retry in range(50):
+
+ # Waiting for BGP to converge
+ logger.info(
+ "Waiting for %s sec for BGP to converge on router" " %s...",
+ sleeptime,
+ router,
+ )
+ sleep(sleeptime)
+
+ show_bgp_json = run_frr_cmd(rnode, "show bgp summary json", isjson=True)
+ # Verifying output dictionary show_bgp_json is empty or not
+ if not bool(show_bgp_json):
+ errormsg = "BGP is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+ total_peer = 0
+ for addr_type in bgp_addr_type.keys():
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor in bgp_neighbors:
+ total_peer += len(bgp_neighbors[bgp_neighbor]["dest_link"])
+
+ no_of_peer = 0
+ for addr_type in bgp_addr_type:
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+ peer_uptime_after_clear_bgp[bgp_neighbor] = ipv4_data[
+ neighbor_ip
+ ]["peerUptimeEstablishedEpoch"]
+ else:
+ ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
+ nh_state = ipv6_data[neighbor_ip]["state"]
+ # Peer up time dictionary
+ peer_uptime_after_clear_bgp[bgp_neighbor] = ipv6_data[
+ neighbor_ip
+ ]["peerUptimeEstablishedEpoch"]
+
+ if nh_state == "Established":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer:
+ logger.info("BGP is Converged for router %s after bgp clear", router)
+ break
+ else:
+ logger.info(
+ "BGP is not yet Converged for router %s after" " bgp clear", router
+ )
+ else:
+ errormsg = (
+ "TIMEOUT!! BGP is not converged in {} seconds for"
+ " router {}".format(retry * sleeptime, router)
+ )
+ return errormsg
+
+ # Comparing peerUptimeEstablishedEpoch dictionaries
+ if peer_uptime_before_clear_bgp != peer_uptime_after_clear_bgp:
+ logger.info("BGP neighborship is reset after clear BGP on router %s", router)
+ else:
+ errormsg = (
+ "BGP neighborship is not reset after clear bgp on router"
+ " {}".format(router)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def verify_bgp_timers_and_functionality(tgen, topo, input_dict):
+ """
+ To verify BGP timer config, execute "show ip bgp neighbor json" command
+ and verify bgp timers with input_dict data.
+ To veirfy bgp timers functonality, shutting down peer interface
+ and verify BGP neighborship status.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type`: ip type, ipv4/ipv6
+ * `input_dict`: defines for which router, bgp timers needs to be verified
+
+ Usage:
+ # To verify BGP timers for neighbor r2 of router r1
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "bgp_neighbors":{
+ "r2":{
+ "keepalivetimer": 5,
+ "holddowntimer": 15,
+ }}}}}
+ result = verify_bgp_timers_and_functionality(tgen, topo, "ipv4",
+ input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ sleep(5)
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+
+ rnode = router_list[router]
+
+ logger.info("Verifying bgp timers functionality, DUT is %s:", router)
+
+ show_ip_bgp_neighbor_json = run_frr_cmd(
+ rnode, "show ip bgp neighbor json", isjson=True
+ )
+
+ bgp_addr_type = input_dict[router]["bgp"]["address_family"]
+
+ for addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ keepalivetimer = peer_dict["keepalivetimer"]
+ holddowntimer = peer_dict["holddowntimer"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+ neighbor_intf = data[dest_link]["interface"]
+
+ # Verify HoldDownTimer for neighbor
+ bgpHoldTimeMsecs = show_ip_bgp_neighbor_json[neighbor_ip][
+ "bgpTimerHoldTimeMsecs"
+ ]
+ if bgpHoldTimeMsecs != holddowntimer * 1000:
+ errormsg = (
+ "Verifying holddowntimer for bgp "
+ "neighbor {} under dut {}, found: {} "
+ "but expected: {}".format(
+ neighbor_ip,
+ router,
+ bgpHoldTimeMsecs,
+ holddowntimer * 1000,
+ )
+ )
+ return errormsg
+
+ # Verify KeepAliveTimer for neighbor
+ bgpKeepAliveTimeMsecs = show_ip_bgp_neighbor_json[neighbor_ip][
+ "bgpTimerKeepAliveIntervalMsecs"
+ ]
+ if bgpKeepAliveTimeMsecs != keepalivetimer * 1000:
+ errormsg = (
+ "Verifying keepalivetimer for bgp "
+ "neighbor {} under dut {}, found: {} "
+ "but expected: {}".format(
+ neighbor_ip,
+ router,
+ bgpKeepAliveTimeMsecs,
+ keepalivetimer * 1000,
+ )
+ )
+ return errormsg
+
+ ####################
+ # Shutting down peer interface after keepalive time and
+ # after some time bringing up peer interface.
+ # verifying BGP neighborship in (hold down-keep alive)
+ # time, it should not go down
+ ####################
+
+ # Wait till keep alive time
+ logger.info("=" * 20)
+ logger.info("Scenario 1:")
+ logger.info(
+ "Shutdown and bring up peer interface: %s "
+ "in keep alive time : %s sec and verify "
+ " BGP neighborship is intact in %s sec ",
+ neighbor_intf,
+ keepalivetimer,
+ (holddowntimer - keepalivetimer),
+ )
+ logger.info("=" * 20)
+ logger.info("Waiting for %s sec..", keepalivetimer)
+ sleep(keepalivetimer)
+
+ # Shutting down peer ineterface
+ logger.info(
+ "Shutting down interface %s on router %s",
+ neighbor_intf,
+ bgp_neighbor,
+ )
+ topotest.interface_set_status(
+ router_list[bgp_neighbor], neighbor_intf, ifaceaction=False
+ )
+
+ # Bringing up peer interface
+ sleep(5)
+ logger.info(
+ "Bringing up interface %s on router %s..",
+ neighbor_intf,
+ bgp_neighbor,
+ )
+ topotest.interface_set_status(
+ router_list[bgp_neighbor], neighbor_intf, ifaceaction=True
+ )
+
+ # Verifying BGP neighborship is intact in
+ # (holddown - keepalive) time
+ for timer in range(
+ keepalivetimer, holddowntimer, int(holddowntimer / 3)
+ ):
+ logger.info("Waiting for %s sec..", keepalivetimer)
+ sleep(keepalivetimer)
+ sleep(2)
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp summary json", isjson=True
+ )
+
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
+ nh_state = ipv6_data[neighbor_ip]["state"]
+
+ if timer == (holddowntimer - keepalivetimer):
+ if nh_state != "Established":
+ errormsg = (
+ "BGP neighborship has not gone "
+ "down in {} sec for neighbor {}".format(
+ timer, bgp_neighbor
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "BGP neighborship is intact in %s"
+ " sec for neighbor %s",
+ timer,
+ bgp_neighbor,
+ )
+
+ ####################
+ # Shutting down peer interface and verifying that BGP
+ # neighborship is going down in holddown time
+ ####################
+ logger.info("=" * 20)
+ logger.info("Scenario 2:")
+ logger.info(
+ "Shutdown peer interface: %s and verify BGP"
+ " neighborship has gone down in hold down "
+ "time %s sec",
+ neighbor_intf,
+ holddowntimer,
+ )
+ logger.info("=" * 20)
+
+ logger.info(
+ "Shutting down interface %s on router %s..",
+ neighbor_intf,
+ bgp_neighbor,
+ )
+ topotest.interface_set_status(
+ router_list[bgp_neighbor], neighbor_intf, ifaceaction=False
+ )
+
+ # Verifying BGP neighborship is going down in holddown time
+ for timer in range(
+ keepalivetimer,
+ (holddowntimer + keepalivetimer),
+ int(holddowntimer / 3),
+ ):
+ logger.info("Waiting for %s sec..", keepalivetimer)
+ sleep(keepalivetimer)
+ sleep(2)
+ show_bgp_json = run_frr_cmd(
+ rnode, "show bgp summary json", isjson=True
+ )
+
+ if addr_type == "ipv4":
+ ipv4_data = show_bgp_json["ipv4Unicast"]["peers"]
+ nh_state = ipv4_data[neighbor_ip]["state"]
+ else:
+ ipv6_data = show_bgp_json["ipv6Unicast"]["peers"]
+ nh_state = ipv6_data[neighbor_ip]["state"]
+
+ if timer == holddowntimer:
+ if nh_state == "Established":
+ errormsg = (
+ "BGP neighborship has not gone "
+ "down in {} sec for neighbor {}".format(
+ timer, bgp_neighbor
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "BGP neighborship has gone down in"
+ " %s sec for neighbor %s",
+ timer,
+ bgp_neighbor,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=16)
+def verify_bgp_attributes(
+ tgen,
+ addr_type,
+ dut,
+ static_routes,
+ rmap_name=None,
+ input_dict=None,
+ seq_id=None,
+ vrf=None,
+ nexthop=None,
+ expected=True,
+):
+ """
+ API will verify BGP attributes set by Route-map for given prefix and
+ DUT. it will run "show bgp ipv4/ipv6 {prefix_address} json" command
+ in DUT to verify BGP attributes set by route-map, Set attributes
+ values will be read from input_dict and verified with command output.
+
+ * `tgen`: topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test
+ * `static_routes`: Static Routes for which BGP set attributes needs to be
+ verified
+ * `rmap_name`: route map name for which set criteria needs to be verified
+ * `input_dict`: defines for which router, AS numbers needs
+ * `seq_id`: sequence number of rmap, default is None
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ # To verify BGP attribute "localpref" set to 150 and "med" set to 30
+ for prefix 10.0.20.1/32 in router r3.
+ input_dict = {
+ "r3": {
+ "route_maps": {
+ "rmap_match_pf_list1": [
+ {
+ "action": "PERMIT",
+ "match": {"prefix_list": "pf_list_1"},
+ "set": {"localpref": 150, "med": 30}
+ }
+ ],
+ },
+ "as_path": "500 400"
+ }
+ }
+ static_routes (list) = ["10.0.20.1/32"]
+
+
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ logger.info("Verifying BGP set attributes for dut {}:".format(router))
+
+ for static_route in static_routes:
+ if vrf:
+ cmd = "show bgp vrf {} {} {} json".format(vrf, addr_type, static_route)
+ else:
+ cmd = "show bgp {} {} json".format(addr_type, static_route)
+ show_bgp_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ dict_to_test = []
+ tmp_list = []
+
+ dict_list = list(input_dict.values())[0]
+
+ if "route_maps" in dict_list:
+ for rmap_router in input_dict.keys():
+ for rmap, values in input_dict[rmap_router]["route_maps"].items():
+ if rmap == rmap_name:
+ dict_to_test = values
+ for rmap_dict in values:
+ if seq_id is not None:
+ if type(seq_id) is not list:
+ seq_id = [seq_id]
+
+ if "seq_id" in rmap_dict:
+ rmap_seq_id = rmap_dict["seq_id"]
+ for _seq_id in seq_id:
+ if _seq_id == rmap_seq_id:
+ tmp_list.append(rmap_dict)
+ if tmp_list:
+ dict_to_test = tmp_list
+
+ value = None
+ for rmap_dict in dict_to_test:
+ if "set" in rmap_dict:
+ for criteria in rmap_dict["set"].keys():
+ found = False
+ for path in show_bgp_json["paths"]:
+ if criteria not in path:
+ continue
+
+ if criteria == "aspath":
+ value = path[criteria]["string"]
+ else:
+ value = path[criteria]
+
+ if rmap_dict["set"][criteria] == value:
+ found = True
+ logger.info(
+ "Verifying BGP "
+ "attribute {} for"
+ " route: {} in "
+ "router: {}, found"
+ " expected value:"
+ " {}".format(
+ criteria,
+ static_route,
+ dut,
+ value,
+ )
+ )
+ break
+
+ if not found:
+ errormsg = (
+ "Failed: Verifying BGP "
+ "attribute {} for route:"
+ " {} in router: {}, "
+ " expected value: {} but"
+ " found: {}".format(
+ criteria,
+ static_route,
+ dut,
+ rmap_dict["set"][criteria],
+ value,
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=8)
+def verify_best_path_as_per_bgp_attribute(
+ tgen, addr_type, router, input_dict, attribute, expected=True
+):
+ """
+ API is to verify best path according to BGP attributes for given routes.
+ "show bgp ipv4/6 json" command will be run and verify best path according
+ to shortest as-path, highest local-preference and med, lowest weight and
+ route origin IGP>EGP>INCOMPLETE.
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `tgen` : topogen object
+ * `attribute` : calculate best path using this attribute
+ * `input_dict`: defines different routes to calculate for which route
+ best path is selected
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ # To verify best path for routes 200.50.2.0/32 and 200.60.2.0/32 from
+ router r7 to router r1(DUT) as per shortest as-path attribute
+ input_dict = {
+ "r7": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "advertise_networks": [
+ {
+ "network": "200.50.2.0/32"
+ },
+ {
+ "network": "200.60.2.0/32"
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ attribute = "locPrf"
+ result = verify_best_path_as_per_bgp_attribute(tgen, "ipv4", dut, \
+ input_dict, attribute)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if router not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[router]
+
+ # Verifying show bgp json
+ command = "show bgp"
+
+ sleep(2)
+ logger.info("Verifying router %s RIB for best path:", router)
+
+ static_route = False
+ advertise_network = False
+ for route_val in input_dict.values():
+ if "static_routes" in route_val:
+ static_route = True
+ networks = route_val["static_routes"]
+ else:
+ advertise_network = True
+ net_data = route_val["bgp"]["address_family"][addr_type]["unicast"]
+ networks = net_data["advertise_networks"]
+
+ for network in networks:
+ _network = network["network"]
+ no_of_ip = network.setdefault("no_of_ip", 1)
+ vrf = network.setdefault("vrf", None)
+
+ if vrf:
+ cmd = "{} vrf {}".format(command, vrf)
+ else:
+ cmd = command
+
+ cmd = "{} {}".format(cmd, addr_type)
+ cmd = "{} json".format(cmd)
+ sh_ip_bgp_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ routes = generate_ips(_network, no_of_ip)
+ for route in routes:
+ route = str(ipaddress.ip_network(frr_unicode(route)))
+
+ if route in sh_ip_bgp_json["routes"]:
+ route_attributes = sh_ip_bgp_json["routes"][route]
+ _next_hop = None
+ compare = None
+ attribute_dict = {}
+ for route_attribute in route_attributes:
+ next_hops = route_attribute["nexthops"]
+ for next_hop in next_hops:
+ next_hop_ip = next_hop["ip"]
+ attribute_dict[next_hop_ip] = route_attribute[attribute]
+
+ # AS_PATH attribute
+ if attribute == "path":
+ # Find next_hop for the route have minimum as_path
+ _next_hop = min(
+ attribute_dict, key=lambda x: len(set(attribute_dict[x]))
+ )
+ compare = "SHORTEST"
+
+ # LOCAL_PREF attribute
+ elif attribute == "locPrf":
+ # Find next_hop for the route have highest local preference
+ _next_hop = max(
+ attribute_dict, key=(lambda k: attribute_dict[k])
+ )
+ compare = "HIGHEST"
+
+ # WEIGHT attribute
+ elif attribute == "weight":
+ # Find next_hop for the route have highest weight
+ _next_hop = max(
+ attribute_dict, key=(lambda k: attribute_dict[k])
+ )
+ compare = "HIGHEST"
+
+ # ORIGIN attribute
+ elif attribute == "origin":
+ # Find next_hop for the route have IGP as origin, -
+ # - rule is IGP>EGP>INCOMPLETE
+ _next_hop = [
+ key
+ for (key, value) in attribute_dict.items()
+ if value == "IGP"
+ ][0]
+ compare = ""
+
+ # MED attribute
+ elif attribute == "metric":
+ # Find next_hop for the route have LOWEST MED
+ _next_hop = min(
+ attribute_dict, key=(lambda k: attribute_dict[k])
+ )
+ compare = "LOWEST"
+
+ # Show ip route
+ if addr_type == "ipv4":
+ command_1 = "show ip route"
+ else:
+ command_1 = "show ipv6 route"
+
+ if vrf:
+ cmd = "{} vrf {} json".format(command_1, vrf)
+ else:
+ cmd = "{} json".format(command_1)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if not bool(rib_routes_json):
+ errormsg = "No route found in RIB of router {}..".format(router)
+ return errormsg
+
+ st_found = False
+ nh_found = False
+ # Find best is installed in RIB
+ if route in rib_routes_json:
+ st_found = True
+ # Verify next_hop in rib_routes_json
+ if (
+ rib_routes_json[route][0]["nexthops"][0]["ip"]
+ in attribute_dict
+ ):
+ nh_found = True
+ else:
+ errormsg = (
+ "Incorrect Nexthop for BGP route {} in "
+ "RIB of router {}, Expected: {}, Found:"
+ " {}\n".format(
+ route,
+ router,
+ rib_routes_json[route][0]["nexthops"][0]["ip"],
+ _next_hop,
+ )
+ )
+ return errormsg
+
+ if st_found and nh_found:
+ logger.info(
+ "Best path for prefix: %s with next_hop: %s is "
+ "installed according to %s %s: (%s) in RIB of "
+ "router %s",
+ route,
+ _next_hop,
+ compare,
+ attribute,
+ attribute_dict[_next_hop],
+ router,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=10)
+def verify_best_path_as_per_admin_distance(
+ tgen, addr_type, router, input_dict, attribute, expected=True, vrf=None
+):
+ """
+ API is to verify best path according to admin distance for given
+ route. "show ip/ipv6 route json" command will be run and verify
+ best path accoring to shortest admin distanc.
+
+ Parameters
+ ----------
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test
+ * `tgen` : topogen object
+ * `attribute` : calculate best path using admin distance
+ * `input_dict`: defines different routes with different admin distance
+ to calculate for which route best path is selected
+ * `expected` : expected results from API, by-default True
+ * `vrf`: Pass vrf name check for perticular vrf.
+
+ Usage
+ -----
+ # To verify best path for route 200.50.2.0/32 from router r2 to
+ router r1(DUT) as per shortest admin distance which is 60.
+ input_dict = {
+ "r2": {
+ "static_routes": [{"network": "200.50.2.0/32", \
+ "admin_distance": 80, "next_hop": "10.0.0.14"},
+ {"network": "200.50.2.0/32", \
+ "admin_distance": 60, "next_hop": "10.0.0.18"}]
+ }}
+ attribute = "locPrf"
+ result = verify_best_path_as_per_admin_distance(tgen, "ipv4", dut, \
+ input_dict, attribute):
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
+ if router not in router_list:
+ return False
+
+ rnode = tgen.routers()[router]
+
+ sleep(5)
+ logger.info("Verifying router %s RIB for best path:", router)
+
+ # Show ip route cmd
+ if addr_type == "ipv4":
+ command = "show ip route"
+ else:
+ command = "show ipv6 route"
+
+ if vrf:
+ command = "{} vrf {} json".format(command, vrf)
+ else:
+ command = "{} json".format(command)
+
+ for routes_from_router in input_dict.keys():
+ sh_ip_route_json = router_list[routes_from_router].vtysh_cmd(
+ command, isjson=True
+ )
+ networks = input_dict[routes_from_router]["static_routes"]
+ for network in networks:
+ route = network["network"]
+
+ route_attributes = sh_ip_route_json[route]
+ _next_hop = None
+ compare = None
+ attribute_dict = {}
+ for route_attribute in route_attributes:
+ next_hops = route_attribute["nexthops"]
+ for next_hop in next_hops:
+ next_hop_ip = next_hop["ip"]
+ attribute_dict[next_hop_ip] = route_attribute["distance"]
+
+ # Find next_hop for the route have LOWEST Admin Distance
+ _next_hop = min(attribute_dict, key=(lambda k: attribute_dict[k]))
+ compare = "LOWEST"
+
+ # Show ip route
+ rib_routes_json = run_frr_cmd(rnode, command, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if not bool(rib_routes_json):
+ errormsg = "No route found in RIB of router {}..".format(router)
+ return errormsg
+
+ st_found = False
+ nh_found = False
+ # Find best is installed in RIB
+ if route in rib_routes_json:
+ st_found = True
+ # Verify next_hop in rib_routes_json
+ if rib_routes_json[route][0]["nexthops"][0]["ip"] == _next_hop:
+ nh_found = True
+ else:
+ errormsg = (
+ "Nexthop {} is Missing for BGP route {}"
+ " in RIB of router {}\n".format(_next_hop, route, router)
+ )
+ return errormsg
+
+ if st_found and nh_found:
+ logger.info(
+ "Best path for prefix: %s is installed according"
+ " to %s %s: (%s) in RIB of router %s",
+ route,
+ compare,
+ attribute,
+ attribute_dict[_next_hop],
+ router,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=30)
+def verify_bgp_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=None,
+ aspath=None,
+ multi_nh=None,
+ expected=True,
+):
+ """
+ This API is to verify whether bgp rib has any
+ matching route for a nexthop.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: input dut router name
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict` : input dict, has details of static routes
+ * `next_hop`[optional]: next_hop which needs to be verified,
+ default = static
+ * 'aspath'[optional]: aspath which needs to be verified
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = 'r1'
+ next_hop = "192.168.1.10"
+ input_dict = topo['routers']
+ aspath = "100 200 300"
+ result = verify_bgp_rib(tgen, addr_type, dut, tgen, input_dict,
+ next_hop, aspath)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: verify_bgp_rib()")
+
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ list1 = []
+ list2 = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ # Verifying RIB routes
+ command = "show bgp"
+
+ # Static routes
+ sleep(2)
+ logger.info("Checking router {} BGP RIB:".format(dut))
+
+ if "static_routes" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ found_routes = []
+ missing_routes = []
+ st_found = False
+ nh_found = False
+
+ vrf = static_route.setdefault("vrf", None)
+ community = static_route.setdefault("community", None)
+ largeCommunity = static_route.setdefault("largeCommunity", None)
+
+ if vrf:
+ cmd = "{} vrf {} {}".format(command, vrf, addr_type)
+
+ if community:
+ cmd = "{} community {}".format(cmd, community)
+
+ if largeCommunity:
+ cmd = "{} large-community {}".format(cmd, largeCommunity)
+ else:
+ cmd = "{} {}".format(command, addr_type)
+
+ cmd = "{} json".format(cmd)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) == False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+
+ network = static_route["network"]
+
+ if "no_of_ip" in static_route:
+ no_of_ip = static_route["no_of_ip"]
+ else:
+ no_of_ip = 1
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json["routes"]:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if next_hop and multi_nh and st_found:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ list1 = next_hop
+
+ for mnh in range(
+ 0, len(rib_routes_json["routes"][st_rt])
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in rib_routes_json["routes"][
+ st_rt
+ ][mnh]["nexthops"]
+ ]
+ )
+ for mnh in found_hops:
+ for each_nh_in_multipath in mnh:
+ list2.append(each_nh_in_multipath)
+ if found_hops[0]:
+ missing_list_of_nexthops = set(list2).difference(
+ list1
+ )
+ additional_nexthops_in_required_nhs = set(
+ list1
+ ).difference(list2)
+
+ if list2:
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ else:
+ nh_found = True
+
+ elif next_hop and multi_nh is None:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ list1 = next_hop
+ found_hops = [
+ rib_r["ip"]
+ for rib_r in rib_routes_json["routes"][st_rt][0][
+ "nexthops"
+ ]
+ ]
+ list2 = found_hops
+ missing_list_of_nexthops = set(list2).difference(list1)
+ additional_nexthops_in_required_nhs = set(
+ list1
+ ).difference(list2)
+
+ if list2:
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+
+ if aspath:
+ found_paths = rib_routes_json["routes"][st_rt][0][
+ "path"
+ ]
+ if aspath == found_paths:
+ aspath_found = True
+ logger.info(
+ "Found AS path {} for route"
+ " {} in RIB of router "
+ "{}\n".format(aspath, st_rt, dut)
+ )
+ else:
+ errormsg = (
+ "AS Path {} is missing for route"
+ "for route {} in RIB of router {}\n".format(
+ aspath, st_rt, dut
+ )
+ )
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info(
+ "Found next_hop {} for all bgp"
+ " routes in RIB of"
+ " router {}\n".format(next_hop, router)
+ )
+
+ if len(missing_routes) > 0:
+ errormsg = (
+ "Missing route in RIB of router {}, "
+ "routes: {}\n".format(dut, missing_routes)
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "Verified routes in router {} BGP RIB, "
+ "found routes are: {} \n".format(dut, found_routes)
+ )
+ continue
+
+ if "bgp" not in input_dict[routerInput]:
+ continue
+
+ # Advertise networks
+ bgp_data_list = input_dict[routerInput]["bgp"]
+
+ if type(bgp_data_list) is not list:
+ bgp_data_list = [bgp_data_list]
+
+ for bgp_data in bgp_data_list:
+ vrf_id = bgp_data.setdefault("vrf", None)
+ if vrf_id:
+ cmd = "{} vrf {} {}".format(command, vrf_id, addr_type)
+ else:
+ cmd = "{} {}".format(command, addr_type)
+
+ cmd = "{} json".format(cmd)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) == False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+
+ bgp_net_advertise = bgp_data["address_family"][addr_type]["unicast"]
+ advertise_network = bgp_net_advertise.setdefault(
+ "advertise_networks", []
+ )
+
+ for advertise_network_dict in advertise_network:
+ found_routes = []
+ missing_routes = []
+ found = False
+
+ network = advertise_network_dict["network"]
+
+ if "no_of_network" in advertise_network_dict:
+ no_of_network = advertise_network_dict["no_of_network"]
+ else:
+ no_of_network = 1
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_network)
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json["routes"]:
+ found = True
+ found_routes.append(st_rt)
+ else:
+ found = False
+ missing_routes.append(st_rt)
+
+ if len(missing_routes) > 0:
+ errormsg = (
+ "Missing route in BGP RIB of router {},"
+ " are: {}\n".format(dut, missing_routes)
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "Verified routes in router {} BGP RIB, found "
+ "routes are: {}\n".format(dut, found_routes)
+ )
+
+ logger.debug("Exiting lib API: verify_bgp_rib()")
+ return True
+
+
+@retry(retry_timeout=10)
+def verify_graceful_restart(
+ tgen, topo, addr_type, input_dict, dut, peer, expected=True
+):
+ """
+ This API is to verify verify_graceful_restart configuration of DUT and
+ cross verify the same from the peer bgp routerrouter.
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict`: input dictionary, have details of Device Under Test, for
+ which user wants to test the data
+ * `dut`: input dut router name
+ * `peer`: input peer router name
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_graceful_restart(tgen, topo, addr_type, input_dict,
+ dut = "r1", peer = 'r2')
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ try:
+ bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"]
+ except TypeError:
+ bgp_addr_type = topo["routers"][dut]["bgp"][0]["address_family"]
+
+ # bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"]
+
+ if addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ if bgp_neighbor != peer:
+ continue
+
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: {}]: Checking bgp graceful-restart show"
+ " o/p {}".format(dut, neighbor_ip)
+ )
+
+ show_bgp_graceful_json = None
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(
+ addr_type, neighbor_ip
+ ),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info(
+ "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Neighbor ip NOT a matched {}".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+
+ lmode = None
+ rmode = None
+ # Local GR mode
+ if "address_family" in input_dict[dut]["bgp"]:
+ bgp_neighbors = input_dict[dut]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][peer]["dest_link"]
+
+ for dest_link, data in bgp_neighbors.items():
+ if (
+ "graceful-restart-helper" in data
+ and data["graceful-restart-helper"]
+ ):
+ lmode = "Helper"
+ elif "graceful-restart" in data and data["graceful-restart"]:
+ lmode = "Restart"
+ elif (
+ "graceful-restart-disable" in data
+ and data["graceful-restart-disable"]
+ ):
+ lmode = "Disable"
+ else:
+ lmode = None
+
+ if lmode is None:
+ if "graceful-restart" in input_dict[dut]["bgp"]:
+
+ if (
+ "graceful-restart" in input_dict[dut]["bgp"]["graceful-restart"]
+ and input_dict[dut]["bgp"]["graceful-restart"][
+ "graceful-restart"
+ ]
+ ):
+ lmode = "Restart*"
+ elif (
+ "graceful-restart-disable"
+ in input_dict[dut]["bgp"]["graceful-restart"]
+ and input_dict[dut]["bgp"]["graceful-restart"][
+ "graceful-restart-disable"
+ ]
+ ):
+ lmode = "Disable*"
+ else:
+ lmode = "Helper*"
+ else:
+ lmode = "Helper*"
+
+ if lmode == "Disable" or lmode == "Disable*":
+ return True
+
+ # Remote GR mode
+ if "address_family" in input_dict[peer]["bgp"]:
+ bgp_neighbors = input_dict[peer]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]["neighbor"][dut]["dest_link"]
+
+ for dest_link, data in bgp_neighbors.items():
+ if (
+ "graceful-restart-helper" in data
+ and data["graceful-restart-helper"]
+ ):
+ rmode = "Helper"
+ elif "graceful-restart" in data and data["graceful-restart"]:
+ rmode = "Restart"
+ elif (
+ "graceful-restart-disable" in data
+ and data["graceful-restart-disable"]
+ ):
+ rmode = "Disable"
+ else:
+ rmode = None
+
+ if rmode is None:
+ if "graceful-restart" in input_dict[peer]["bgp"]:
+
+ if (
+ "graceful-restart"
+ in input_dict[peer]["bgp"]["graceful-restart"]
+ and input_dict[peer]["bgp"]["graceful-restart"][
+ "graceful-restart"
+ ]
+ ):
+ rmode = "Restart"
+ elif (
+ "graceful-restart-disable"
+ in input_dict[peer]["bgp"]["graceful-restart"]
+ and input_dict[peer]["bgp"]["graceful-restart"][
+ "graceful-restart-disable"
+ ]
+ ):
+ rmode = "Disable"
+ else:
+ rmode = "Helper"
+ else:
+ rmode = "Helper"
+
+ if show_bgp_graceful_json_out["localGrMode"] == lmode:
+ logger.info(
+ "[DUT: {}]: localGrMode : {} ".format(
+ dut, show_bgp_graceful_json_out["localGrMode"]
+ )
+ )
+ else:
+ errormsg = (
+ "[DUT: {}]: localGrMode is not correct"
+ " Expected: {}, Found: {}".format(
+ dut, lmode, show_bgp_graceful_json_out["localGrMode"]
+ )
+ )
+ return errormsg
+
+ if show_bgp_graceful_json_out["remoteGrMode"] == rmode:
+ logger.info(
+ "[DUT: {}]: remoteGrMode : {} ".format(
+ dut, show_bgp_graceful_json_out["remoteGrMode"]
+ )
+ )
+ elif (
+ show_bgp_graceful_json_out["remoteGrMode"] == "NotApplicable"
+ and rmode == "Disable"
+ ):
+ logger.info(
+ "[DUT: {}]: remoteGrMode : {} ".format(
+ dut, show_bgp_graceful_json_out["remoteGrMode"]
+ )
+ )
+ else:
+ errormsg = (
+ "[DUT: {}]: remoteGrMode is not correct"
+ " Expected: {}, Found: {}".format(
+ dut, rmode, show_bgp_graceful_json_out["remoteGrMode"]
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=10)
+def verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
+ """
+ This API is to verify r_bit in the BGP gr capability advertised
+ by the neighbor router
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict`: input dictionary, have details of Device Under Test, for
+ which user wants to test the data
+ * `dut`: input dut router name
+ * `peer`: peer name
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = verify_r_bit(tgen, topo, addr_type, input_dict, dut, peer)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+
+ if addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ if bgp_neighbor != peer:
+ continue
+
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: {}]: Checking bgp graceful-restart show"
+ " o/p {}".format(dut, neighbor_ip)
+ )
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(
+ addr_type, neighbor_ip
+ ),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info(
+ "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Neighbor ip NOT a matched {}".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+
+ if "rBit" in show_bgp_graceful_json_out:
+ if show_bgp_graceful_json_out["rBit"]:
+ logger.info("[DUT: {}]: Rbit true {}".format(dut, neighbor_ip))
+ else:
+ errormsg = "[DUT: {}]: Rbit false {}".format(dut, neighbor_ip)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=10)
+def verify_eor(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
+ """
+ This API is to verify EOR
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict`: input dictionary, have details of DUT, for
+ which user wants to test the data
+ * `dut`: input dut router name
+ * `peer`: peer name
+ Usage
+ -----
+ input_dict = {
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_eor(tgen, topo, addr_type, input_dict, dut, peer)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+
+ if addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ if bgp_neighbor != peer:
+ continue
+
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: %s]: Checking bgp graceful-restart" " show o/p %s",
+ dut,
+ neighbor_ip,
+ )
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(
+ addr_type, neighbor_ip
+ ),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info("[DUT: %s]: Neighbor ip matched %s", dut, neighbor_ip)
+ else:
+ errormsg = "[DUT: %s]: Neighbor ip is NOT matched %s" % (
+ dut,
+ neighbor_ip,
+ )
+ return errormsg
+
+ if addr_type == "ipv4":
+ afi = "ipv4Unicast"
+ elif addr_type == "ipv6":
+ afi = "ipv6Unicast"
+ else:
+ errormsg = "Address type %s is not supported" % (addr_type)
+ return errormsg
+
+ eor_json = show_bgp_graceful_json_out[afi]["endOfRibStatus"]
+ if "endOfRibSend" in eor_json:
+
+ if eor_json["endOfRibSend"]:
+ logger.info(
+ "[DUT: %s]: EOR Send true for %s " "%s", dut, neighbor_ip, afi
+ )
+ else:
+ errormsg = "[DUT: %s]: EOR Send false for %s" " %s" % (
+ dut,
+ neighbor_ip,
+ afi,
+ )
+ return errormsg
+
+ if "endOfRibRecv" in eor_json:
+ if eor_json["endOfRibRecv"]:
+ logger.info(
+ "[DUT: %s]: EOR Recv true %s " "%s", dut, neighbor_ip, afi
+ )
+ else:
+ errormsg = "[DUT: %s]: EOR Recv false %s " "%s" % (
+ dut,
+ neighbor_ip,
+ afi,
+ )
+ return errormsg
+
+ if "endOfRibSentAfterUpdate" in eor_json:
+ if eor_json["endOfRibSentAfterUpdate"]:
+ logger.info(
+ "[DUT: %s]: EOR SendTime true for %s" " %s",
+ dut,
+ neighbor_ip,
+ afi,
+ )
+ else:
+ errormsg = "[DUT: %s]: EOR SendTime false for " "%s %s" % (
+ dut,
+ neighbor_ip,
+ afi,
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=8)
+def verify_f_bit(tgen, topo, addr_type, input_dict, dut, peer, expected=True):
+ """
+ This API is to verify f_bit in the BGP gr capability advertised
+ by the neighbor router
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict`: input dictionary, have details of Device Under Test, for
+ which user wants to test the data
+ * `dut`: input dut router name
+ * `peer`: peer name
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link":{
+ "r1": {
+ "graceful-restart": True
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_f_bit(tgen, topo, 'ipv4', input_dict, dut, peer)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ bgp_addr_type = topo["routers"][router]["bgp"]["address_family"]
+
+ if addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ if bgp_neighbor != peer:
+ continue
+
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: {}]: Checking bgp graceful-restart show"
+ " o/p {}".format(dut, neighbor_ip)
+ )
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(
+ addr_type, neighbor_ip
+ ),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info(
+ "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Neighbor ip NOT a match {}".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+
+ if "ipv4Unicast" in show_bgp_graceful_json_out:
+ if show_bgp_graceful_json_out["ipv4Unicast"]["fBit"]:
+ logger.info(
+ "[DUT: {}]: Fbit True for {} IPv4"
+ " Unicast".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Fbit False for {} IPv4" " Unicast".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+
+ elif "ipv6Unicast" in show_bgp_graceful_json_out:
+ if show_bgp_graceful_json_out["ipv6Unicast"]["fBit"]:
+ logger.info(
+ "[DUT: {}]: Fbit True for {} IPv6"
+ " Unicast".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Fbit False for {} IPv6" " Unicast".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+ else:
+ show_bgp_graceful_json_out["ipv4Unicast"]
+ show_bgp_graceful_json_out["ipv6Unicast"]
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=10)
+def verify_graceful_restart_timers(tgen, topo, addr_type, input_dict, dut, peer):
+ """
+ This API is to verify graceful restart timers, configured and received
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `input_dict`: input dictionary, have details of Device Under Test,
+ for which user wants to test the data
+ * `dut`: input dut router name
+ * `peer`: peer name
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ # Configure graceful-restart
+ input_dict_1 = {
+ "r1": {
+ "bgp": {
+ "bgp_neighbors": {
+ "r3": {
+ "graceful-restart": "graceful-restart-helper"
+ }
+ },
+ "gracefulrestart": ["restart-time 150"]
+ }
+ },
+ "r3": {
+ "bgp": {
+ "bgp_neighbors": {
+ "r1": {
+ "graceful-restart": "graceful-restart"
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_graceful_restart_timers(tgen, topo, 'ipv4', input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router, rnode in tgen.routers().items():
+ if router != dut:
+ continue
+
+ bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"]
+
+ if addr_type in bgp_addr_type:
+ if not check_address_types(addr_type):
+ continue
+
+ bgp_neighbors = bgp_addr_type[addr_type]["unicast"]["neighbor"]
+
+ for bgp_neighbor, peer_data in bgp_neighbors.items():
+ if bgp_neighbor != peer:
+ continue
+
+ for dest_link, peer_dict in peer_data["dest_link"].items():
+ data = topo["routers"][bgp_neighbor]["links"]
+
+ if dest_link in data:
+ neighbor_ip = data[dest_link][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: {}]: Checking bgp graceful-restart show"
+ " o/p {}".format(dut, neighbor_ip)
+ )
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(
+ addr_type, neighbor_ip
+ ),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info(
+ "[DUT: {}]: Neighbor ip matched {}".format(dut, neighbor_ip)
+ )
+ else:
+ errormsg = "[DUT: {}]: Neighbor ip is NOT matched {}".format(
+ dut, neighbor_ip
+ )
+ return errormsg
+
+ # Graceful-restart timer
+ if "graceful-restart" in input_dict[peer]["bgp"]:
+ if "timer" in input_dict[peer]["bgp"]["graceful-restart"]:
+ for rs_timer, value in input_dict[peer]["bgp"]["graceful-restart"][
+ "timer"
+ ].items():
+ if rs_timer == "restart-time":
+
+ receivedTimer = value
+ if (
+ show_bgp_graceful_json_out["timers"][
+ "receivedRestartTimer"
+ ]
+ == receivedTimer
+ ):
+ logger.info(
+ "receivedRestartTimer is {}"
+ " on {} from peer {}".format(
+ receivedTimer, router, peer
+ )
+ )
+ else:
+ errormsg = (
+ "receivedRestartTimer is not"
+ " as expected {}".format(receivedTimer)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=8)
+def verify_gr_address_family(
+ tgen, topo, addr_type, addr_family, dut, peer, expected=True
+):
+ """
+ This API is to verify gr_address_family in the BGP gr capability advertised
+ by the neighbor router
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: input json file data
+ * `addr_type` : ip type ipv4/ipv6
+ * `addr_type` : ip type IPV4 Unicast/IPV6 Unicast
+ * `dut`: input dut router name
+ * `peer`: input peer router to check
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+
+ result = verify_gr_address_family(tgen, topo, "ipv4", "ipv4Unicast", "r1", "r3")
+
+ Returns
+ -------
+ errormsg(str) or None
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if not check_address_types(addr_type):
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return
+
+ routers = tgen.routers()
+ if dut not in routers:
+ return "{} not in routers".format(dut)
+
+ rnode = routers[dut]
+ bgp_addr_type = topo["routers"][dut]["bgp"]["address_family"]
+
+ if addr_type not in bgp_addr_type:
+ return "{} not in bgp_addr_types".format(addr_type)
+
+ if peer not in bgp_addr_type[addr_type]["unicast"]["neighbor"]:
+ return "{} not a peer of {} over {}".format(peer, dut, addr_type)
+
+ nbr_links = topo["routers"][peer]["links"]
+ if dut not in nbr_links or addr_type not in nbr_links[dut]:
+ return "peer {} missing back link to {} over {}".format(peer, dut, addr_type)
+
+ neighbor_ip = nbr_links[dut][addr_type].split("/")[0]
+
+ logger.info(
+ "[DUT: {}]: Checking bgp graceful-restart show o/p {} for {}".format(
+ dut, neighbor_ip, addr_family
+ )
+ )
+
+ show_bgp_graceful_json = run_frr_cmd(
+ rnode,
+ "show bgp {} neighbor {} graceful-restart json".format(addr_type, neighbor_ip),
+ isjson=True,
+ )
+
+ show_bgp_graceful_json_out = show_bgp_graceful_json[neighbor_ip]
+
+ if show_bgp_graceful_json_out["neighborAddr"] == neighbor_ip:
+ logger.info("Neighbor ip matched {}".format(neighbor_ip))
+ else:
+ errormsg = "Neighbor ip NOT a match {}".format(neighbor_ip)
+ return errormsg
+
+ if addr_family == "ipv4Unicast":
+ if "ipv4Unicast" in show_bgp_graceful_json_out:
+ logger.info("ipv4Unicast present for {} ".format(neighbor_ip))
+ return True
+ else:
+ errormsg = "ipv4Unicast NOT present for {} ".format(neighbor_ip)
+ return errormsg
+
+ elif addr_family == "ipv6Unicast":
+ if "ipv6Unicast" in show_bgp_graceful_json_out:
+ logger.info("ipv6Unicast present for {} ".format(neighbor_ip))
+ return True
+ else:
+ errormsg = "ipv6Unicast NOT present for {} ".format(neighbor_ip)
+ return errormsg
+ else:
+ errormsg = "Aaddress family: {} present for {} ".format(
+ addr_family, neighbor_ip
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+@retry(retry_timeout=12)
+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,
+ expected=True,
+):
+ """
+ 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
+ * `expected` : expected results from API, by-default True
+
+ 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 Expected: %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"]
+
+ found = False
+ rd = "{}:{}".format(router_id, vni_dict[vrf])
+ for _rd, rd_value_json in evpn_rd_value_json.items():
+ if (
+ str(rd_value_json["rd"].split(":")[0])
+ != rd.split(":")[0]
+ ):
+ continue
+
+ if int(rd_value_json["rd"].split(":")[1]) > 0:
+ found = True
+
+ if found:
+ logger.info(
+ "[DUT %s]: Verifying RD value for"
+ " EVPN route: %s "
+ "Found Expected: %s",
+ dut,
+ route,
+ rd_value_json["rd"],
+ )
+ return True
+ else:
+ errormsg = (
+ "[DUT: %s] Failed: Verifying"
+ " RD value for EVPN route: %s"
+ " FOUND : %s" % (dut, route, rd_value_json["rd"])
+ )
+ return errormsg
+
+ if rt == "auto":
+ vni_dict = {}
+ logger.info(
+ "[DUT: %s]: Verifying auto-rt value for " "evpn route %s:",
+ dut,
+ route,
+ )
+
+ if rt_peer:
+ 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 Expected: %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 Expected: %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 Expected: %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 Expected: %s",
+ dut,
+ _rd,
+ route,
+ ipLen,
+ )
+ return True
+
+ else:
+ errormsg = (
+ "[DUT: %s] RD: %s, Route : %s "
+ "is not present in cli json "
+ "output " % (dut, _rd, route)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+@retry(retry_timeout=10)
+def verify_evpn_routes(
+ tgen, topo, dut, input_dict, routeType=5, EthTag=0, next_hop=None, expected=True
+):
+ """
+ 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
+ * `expected` : expected results from API, by-default True
+
+ 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 type(route_data_json) is 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 type(route_data_json) is 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
+
+
+@retry(retry_timeout=10)
+def verify_bgp_bestpath(tgen, addr_type, input_dict):
+ """
+ Verifies bgp next hop values in best-path output
+
+ * `dut` : device under test
+ * `addr_type` : Address type ipv4/ipv6
+ * `input_dict`: having details like multipath and bestpath
+
+ Usage
+ -----
+ input_dict_1 = {
+ "r1": {
+ "ipv4" : {
+ "bestpath": "50.0.0.1",
+ "multipath": ["50.0.0.1", "50.0.0.2"],
+ "network": "100.0.0.0/24"
+ }
+ "ipv6" : {
+ "bestpath": "1000::1",
+ "multipath": ["1000::1", "1000::2"]
+ "network": "2000::1/128"
+ }
+ }
+ }
+
+ result = verify_bgp_bestpath(tgen, input_dict)
+
+ """
+
+ result = False
+ 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 bgp bestpath and multipath " "routes:", dut)
+ result = False
+ for network_dict in input_dict[dut][addr_type]:
+ nw_addr = network_dict.setdefault("network", None)
+ vrf = network_dict.setdefault("vrf", None)
+ bestpath = network_dict.setdefault("bestpath", None)
+
+ if vrf:
+ cmd = "show bgp vrf {} {} {} bestpath json".format(
+ vrf, addr_type, nw_addr
+ )
+ else:
+ cmd = "show bgp {} {} bestpath json".format(addr_type, nw_addr)
+
+ data = run_frr_cmd(rnode, cmd, isjson=True)
+ route = data["paths"][0]
+
+ if "bestpath" in route:
+ if route["bestpath"]["overall"] is True:
+ _bestpath = route["nexthops"][0]["ip"]
+
+ if _bestpath != bestpath:
+ return (
+ "DUT:[{}] Bestpath do not match for"
+ " network: {}, Expected "
+ " {} as bgp bestpath found {}".format(
+ dut, nw_addr, bestpath, _bestpath
+ )
+ )
+
+ logger.info(
+ "DUT:[{}] Found expected bestpath: "
+ " {} for network: {}".format(dut, _bestpath, nw_addr)
+ )
+ result = True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def verify_tcp_mss(tgen, dut, neighbour, configured_tcp_mss, vrf=None):
+ """
+ This api is used to verify the tcp-mss value assigned to a neigbour of DUT
+
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `dut`: device under test
+ * `neighbour`:neigbout IP address
+ * `configured_tcp_mss`:The TCP-MSS value to be verified
+ * `vrf`:vrf
+
+ Usage
+ -----
+ result = verify_tcp_mss(tgen, dut,neighbour,configured_tcp_mss)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ rnode = tgen.routers()[dut]
+ if vrf:
+ cmd = "show bgp vrf {} neighbors {} json".format(vrf, neighbour)
+ else:
+ cmd = "show bgp neighbors {} json".format(neighbour)
+
+ # Execute the command
+ show_vrf_stats = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verify TCP-MSS on router
+ logger.info("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ errormsg = "Core observed while running CLI: %s" % (cmd)
+ return errormsg
+ else:
+ if configured_tcp_mss == show_vrf_stats.get(neighbour).get(
+ "bgpTcpMssConfigured"
+ ):
+ logger.debug(
+ "Configured TCP - MSS Found: {}".format(sys._getframe().f_code.co_name)
+ )
+ return True
+ else:
+ logger.debug(
+ "TCP-MSS Mismatch ,configured {} expecting {}".format(
+ show_vrf_stats.get(neighbour).get("bgpTcpMssConfigured"),
+ configured_tcp_mss,
+ )
+ )
+ return "TCP-MSS Mismatch"
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return False
+
+
+def get_dut_as_number(tgen, dut):
+ """
+ API to get the Autonomous Number of the given DUT
+
+ params:
+ =======
+ dut : Device Under test
+
+ returns :
+ =======
+ Success : DUT Autonomous number
+ Fail : Error message with Boolean False
+ """
+ tgen = get_topogen()
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+ show_bgp_json = run_frr_cmd(rnode, "sh ip bgp summary json ", isjson=True)
+ as_number = show_bgp_json["ipv4Unicast"]["as"]
+ if as_number:
+ logger.info(
+ "[dut {}] DUT contains Automnomous number :: {} ".format(
+ dut, as_number
+ )
+ )
+ return as_number
+ else:
+ logger.error(
+ "[dut {}] ERROR....! DUT doesnot contain any Automnomous number ".format(
+ dut
+ )
+ )
+ return False
+
+
+def get_prefix_count_route(
+ tgen, topo, dut, peer, vrf=None, link=None, sent=None, received=None
+):
+ """
+ API to return the prefix count of default originate the given DUT
+ dut : Device under test
+ peer : neigbor on which you are expecting the route to be received
+
+ returns :
+ prefix_count as dict with ipv4 and ipv6 value
+ """
+ # the neighbor IP address can be accessable by finding the neigborship (vice-versa)
+
+ if link:
+ neighbor_ipv4_address = topo["routers"][peer]["links"][link]["ipv4"]
+ neighbor_ipv6_address = topo["routers"][peer]["links"][link]["ipv6"]
+ else:
+ neighbor_ipv4_address = topo["routers"][peer]["links"][dut]["ipv4"]
+ neighbor_ipv6_address = topo["routers"][peer]["links"][dut]["ipv6"]
+
+ neighbor_ipv4_address = neighbor_ipv4_address.split("/")[0]
+ neighbor_ipv6_address = neighbor_ipv6_address.split("/")[0]
+ prefix_count = {}
+ tgen = get_topogen()
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+
+ if vrf:
+ ipv4_cmd = "sh ip bgp vrf {} summary json".format(vrf)
+ show_bgp_json_ipv4 = run_frr_cmd(rnode, ipv4_cmd, isjson=True)
+ ipv6_cmd = "sh ip bgp vrf {} ipv6 unicast summary json".format(vrf)
+ show_bgp_json_ipv6 = run_frr_cmd(rnode, ipv6_cmd, isjson=True)
+
+ prefix_count["ipv4_count"] = show_bgp_json_ipv4["ipv4Unicast"]["peers"][
+ neighbor_ipv4_address
+ ]["pfxRcd"]
+ prefix_count["ipv6_count"] = show_bgp_json_ipv6["peers"][
+ neighbor_ipv6_address
+ ]["pfxRcd"]
+
+ logger.info(
+ "The Prefix Count of the [DUT:{} : vrf [{}] ] towards neighbor ipv4 : {} and ipv6 : {} is : {}".format(
+ dut,
+ vrf,
+ neighbor_ipv4_address,
+ neighbor_ipv6_address,
+ prefix_count,
+ )
+ )
+ return prefix_count
+
+ else:
+ show_bgp_json_ipv4 = run_frr_cmd(
+ rnode, "sh ip bgp summary json ", isjson=True
+ )
+ show_bgp_json_ipv6 = run_frr_cmd(
+ rnode, "sh ip bgp ipv6 unicast summary json ", isjson=True
+ )
+ if received:
+ prefix_count["ipv4_count"] = show_bgp_json_ipv4["ipv4Unicast"][
+ "peers"
+ ][neighbor_ipv4_address]["pfxRcd"]
+ prefix_count["ipv6_count"] = show_bgp_json_ipv6["peers"][
+ neighbor_ipv6_address
+ ]["pfxRcd"]
+
+ elif sent:
+ prefix_count["ipv4_count"] = show_bgp_json_ipv4["ipv4Unicast"][
+ "peers"
+ ][neighbor_ipv4_address]["pfxSnt"]
+ prefix_count["ipv6_count"] = show_bgp_json_ipv6["peers"][
+ neighbor_ipv6_address
+ ]["pfxSnt"]
+
+ else:
+ prefix_count["ipv4_count"] = show_bgp_json_ipv4["ipv4Unicast"][
+ "peers"
+ ][neighbor_ipv4_address]["pfxRcd"]
+ prefix_count["ipv6_count"] = show_bgp_json_ipv6["peers"][
+ neighbor_ipv6_address
+ ]["pfxRcd"]
+
+ logger.info(
+ "The Prefix Count of the DUT:{} towards neighbor ipv4 : {} and ipv6 : {} is : {}".format(
+ dut, neighbor_ipv4_address, neighbor_ipv6_address, prefix_count
+ )
+ )
+ return prefix_count
+ else:
+ logger.error("ERROR...! Unknown dut {} in topolgy".format(dut))
+
+
+@retry(retry_timeout=5)
+def verify_rib_default_route(
+ tgen,
+ topo,
+ dut,
+ routes,
+ expected_nexthop,
+ metric=None,
+ origin=None,
+ locPrf=None,
+ expected_aspath=None,
+):
+ """
+ API to verify the the 'Default route" in BGP RIB with the attributes the rout carries (metric , local preference, )
+
+ param
+ =====
+ dut : device under test
+ routes : default route with expected nexthop
+ expected_nexthop : the nexthop that is expected the deafult route
+
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+ connected_routes = {}
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+
+ ipv4_routes = run_frr_cmd(rnode, "sh ip bgp json", isjson=True)
+ ipv6_routes = run_frr_cmd(rnode, "sh ip bgp ipv6 unicast json", isjson=True)
+ is_ipv4_default_attrib_found = False
+ is_ipv6_default_attrib_found = False
+
+ default_ipv4_route = routes["ipv4"]
+ default_ipv6_route = "::/0"
+ ipv4_route_Origin = False
+ ipv4_route_local_pref = False
+ ipv4_route_metric = False
+
+ if default_ipv4_route in ipv4_routes["routes"].keys():
+ nxt_hop_count = len(ipv4_routes["routes"][default_ipv4_route])
+ rib_next_hops = []
+ for index in range(nxt_hop_count):
+ rib_next_hops.append(
+ ipv4_routes["routes"][default_ipv4_route][index]["nexthops"][0]["ip"]
+ )
+
+ for nxt_hop in expected_nexthop.items():
+ if nxt_hop[0] == "ipv4":
+ if nxt_hop[1] in rib_next_hops:
+ logger.info(
+ "Default routes [{}] obtained from {} .....PASSED".format(
+ default_ipv4_route, nxt_hop[1]
+ )
+ )
+ else:
+ logger.error(
+ "ERROR ...! Default routes [{}] expected is missing {}".format(
+ default_ipv4_route, nxt_hop[1]
+ )
+ )
+ return False
+
+ else:
+ pass
+
+ if "origin" in ipv4_routes["routes"][default_ipv4_route][0].keys():
+ ipv4_route_Origin = ipv4_routes["routes"][default_ipv4_route][0]["origin"]
+ if "locPrf" in ipv4_routes["routes"][default_ipv4_route][0].keys():
+ ipv4_route_local_pref = ipv4_routes["routes"][default_ipv4_route][0][
+ "locPrf"
+ ]
+ if "metric" in ipv4_routes["routes"][default_ipv4_route][0].keys():
+ ipv4_route_metric = ipv4_routes["routes"][default_ipv4_route][0]["metric"]
+ else:
+ logger.error("ERROR [ DUT {}] : The Default Route Not found in RIB".format(dut))
+ return False
+
+ origin_found = False
+ locPrf_found = False
+ metric_found = False
+ as_path_found = False
+
+ if origin:
+ if origin == ipv4_route_Origin:
+ logger.info(
+ "Dafault Route {} expected origin {} Found in RIB....PASSED".format(
+ default_ipv4_route, origin
+ )
+ )
+ origin_found = True
+ else:
+ logger.error(
+ "ERROR... IPV4::! Expected Origin is {} obtained {}".format(
+ origin, ipv4_route_Origin
+ )
+ )
+ return False
+ else:
+ origin_found = True
+
+ if locPrf:
+ if locPrf == ipv4_route_local_pref:
+ logger.info(
+ "Dafault Route {} expected local preference {} Found in RIB....PASSED".format(
+ default_ipv4_route, locPrf
+ )
+ )
+ locPrf_found = True
+ else:
+ logger.error(
+ "ERROR... IPV4::! Expected Local preference is {} obtained {}".format(
+ locPrf, ipv4_route_local_pref
+ )
+ )
+ return False
+ else:
+ locPrf_found = True
+
+ if metric:
+ if metric == ipv4_route_metric:
+ logger.info(
+ "Dafault Route {} expected metric {} Found in RIB....PASSED".format(
+ default_ipv4_route, metric
+ )
+ )
+
+ metric_found = True
+ else:
+ logger.error(
+ "ERROR... IPV4::! Expected metric is {} obtained {}".format(
+ metric, ipv4_route_metric
+ )
+ )
+ return False
+ else:
+ metric_found = True
+
+ if expected_aspath:
+ obtained_aspath = ipv4_routes["routes"]["0.0.0.0/0"][0]["path"]
+ if expected_aspath in obtained_aspath:
+ as_path_found = True
+ logger.info(
+ "Dafault Route {} expected AS path {} Found in RIB....PASSED".format(
+ default_ipv4_route, expected_aspath
+ )
+ )
+ else:
+ logger.error(
+ "ERROR.....! Expected AS path {} obtained {}..... FAILED ".format(
+ expected_aspath, obtained_aspath
+ )
+ )
+ return False
+ else:
+ as_path_found = True
+
+ if origin_found and locPrf_found and metric_found and as_path_found:
+ is_ipv4_default_attrib_found = True
+ logger.info(
+ "IPV4:: Expected origin ['{}'] , Local Preference ['{}'] , Metric ['{}'] and AS path [{}] is found in RIB".format(
+ origin, locPrf, metric, expected_aspath
+ )
+ )
+ else:
+ is_ipv4_default_attrib_found = False
+ logger.error(
+ "IPV4:: Expected origin ['{}'] Obtained [{}]".format(
+ origin, ipv4_route_Origin
+ )
+ )
+ logger.error(
+ "IPV4:: Expected locPrf ['{}'] Obtained [{}]".format(
+ locPrf, ipv4_route_local_pref
+ )
+ )
+ logger.error(
+ "IPV4:: Expected metric ['{}'] Obtained [{}]".format(
+ metric, ipv4_route_metric
+ )
+ )
+ logger.error(
+ "IPV4:: Expected metric ['{}'] Obtained [{}]".format(
+ expected_aspath, obtained_aspath
+ )
+ )
+
+ route_Origin = False
+ route_local_pref = False
+ route_local_metric = False
+ default_ipv6_route = ""
+ try:
+ ipv6_routes["routes"]["0::0/0"]
+ default_ipv6_route = "0::0/0"
+ except:
+ ipv6_routes["routes"]["::/0"]
+ default_ipv6_route = "::/0"
+ if default_ipv6_route in ipv6_routes["routes"].keys():
+ nxt_hop_count = len(ipv6_routes["routes"][default_ipv6_route])
+ rib_next_hops = []
+ for index in range(nxt_hop_count):
+ rib_next_hops.append(
+ ipv6_routes["routes"][default_ipv6_route][index]["nexthops"][0]["ip"]
+ )
+ try:
+ rib_next_hops.append(
+ ipv6_routes["routes"][default_ipv6_route][index]["nexthops"][1][
+ "ip"
+ ]
+ )
+ except (KeyError, IndexError) as e:
+ logger.error("NO impact ..! Global IPV6 Address not found ")
+
+ for nxt_hop in expected_nexthop.items():
+ if nxt_hop[0] == "ipv6":
+ if nxt_hop[1] in rib_next_hops:
+ logger.info(
+ "Default routes [{}] obtained from {} .....PASSED".format(
+ default_ipv6_route, nxt_hop[1]
+ )
+ )
+ else:
+ logger.error(
+ "ERROR ...! Default routes [{}] expected from {} obtained {}".format(
+ default_ipv6_route, nxt_hop[1], rib_next_hops
+ )
+ )
+ return False
+
+ else:
+ pass
+ if "origin" in ipv6_routes["routes"][default_ipv6_route][0].keys():
+ route_Origin = ipv6_routes["routes"][default_ipv6_route][0]["origin"]
+ if "locPrf" in ipv6_routes["routes"][default_ipv6_route][0].keys():
+ route_local_pref = ipv6_routes["routes"][default_ipv6_route][0]["locPrf"]
+ if "metric" in ipv6_routes["routes"][default_ipv6_route][0].keys():
+ route_local_metric = ipv6_routes["routes"][default_ipv6_route][0]["metric"]
+
+ origin_found = False
+ locPrf_found = False
+ metric_found = False
+ as_path_found = False
+
+ if origin:
+ if origin == route_Origin:
+ logger.info(
+ "Dafault Route {} expected origin {} Found in RIB....PASSED".format(
+ default_ipv6_route, route_Origin
+ )
+ )
+ origin_found = True
+ else:
+ logger.error(
+ "ERROR... IPV6::! Expected Origin is {} obtained {}".format(
+ origin, route_Origin
+ )
+ )
+ return False
+ else:
+ origin_found = True
+
+ if locPrf:
+ if locPrf == route_local_pref:
+ logger.info(
+ "Dafault Route {} expected Local Preference {} Found in RIB....PASSED".format(
+ default_ipv6_route, route_local_pref
+ )
+ )
+ locPrf_found = True
+ else:
+ logger.error(
+ "ERROR... IPV6::! Expected Local Preference is {} obtained {}".format(
+ locPrf, route_local_pref
+ )
+ )
+ return False
+ else:
+ locPrf_found = True
+
+ if metric:
+ if metric == route_local_metric:
+ logger.info(
+ "Dafault Route {} expected metric {} Found in RIB....PASSED".format(
+ default_ipv4_route, metric
+ )
+ )
+
+ metric_found = True
+ else:
+ logger.error(
+ "ERROR... IPV6::! Expected metric is {} obtained {}".format(
+ metric, route_local_metric
+ )
+ )
+ return False
+ else:
+ metric_found = True
+
+ if expected_aspath:
+ obtained_aspath = ipv6_routes["routes"]["::/0"][0]["path"]
+ if expected_aspath in obtained_aspath:
+ as_path_found = True
+ logger.info(
+ "Dafault Route {} expected AS path {} Found in RIB....PASSED".format(
+ default_ipv4_route, expected_aspath
+ )
+ )
+ else:
+ logger.error(
+ "ERROR.....! Expected AS path {} obtained {}..... FAILED ".format(
+ expected_aspath, obtained_aspath
+ )
+ )
+ return False
+ else:
+ as_path_found = True
+
+ if origin_found and locPrf_found and metric_found and as_path_found:
+ is_ipv6_default_attrib_found = True
+ logger.info(
+ "IPV6:: Expected origin ['{}'] , Local Preference ['{}'] , Metric ['{}'] and AS path [{}] is found in RIB".format(
+ origin, locPrf, metric, expected_aspath
+ )
+ )
+ else:
+ is_ipv6_default_attrib_found = False
+ logger.error(
+ "IPV6:: Expected origin ['{}'] Obtained [{}]".format(origin, route_Origin)
+ )
+ logger.error(
+ "IPV6:: Expected locPrf ['{}'] Obtained [{}]".format(
+ locPrf, route_local_pref
+ )
+ )
+ logger.error(
+ "IPV6:: Expected metric ['{}'] Obtained [{}]".format(
+ metric, route_local_metric
+ )
+ )
+ logger.error(
+ "IPV6:: Expected metric ['{}'] Obtained [{}]".format(
+ expected_aspath, obtained_aspath
+ )
+ )
+
+ if is_ipv4_default_attrib_found and is_ipv6_default_attrib_found:
+ logger.info("The attributes are found for default route in RIB ")
+ return True
+ else:
+ return False
+
+
+@retry(retry_timeout=5)
+def verify_fib_default_route(tgen, topo, dut, routes, expected_nexthop):
+ """
+ API to verify the the 'Default route" in FIB
+
+ param
+ =====
+ dut : device under test
+ routes : default route with expected nexthop
+ expected_nexthop : the nexthop that is expected the deafult route
+
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+ connected_routes = {}
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+ ipv4_routes = run_frr_cmd(rnode, "sh ip route json", isjson=True)
+ ipv6_routes = run_frr_cmd(rnode, "sh ipv6 route json", isjson=True)
+
+ is_ipv4_default_route_found = False
+ is_ipv6_default_route_found = False
+ if routes["ipv4"] in ipv4_routes.keys():
+ rib_ipv4_nxt_hops = []
+ ipv4_default_route = routes["ipv4"]
+ nxt_hop_count = len(ipv4_routes[ipv4_default_route][0]["nexthops"])
+ for index in range(nxt_hop_count):
+ rib_ipv4_nxt_hops.append(
+ ipv4_routes[ipv4_default_route][0]["nexthops"][index]["ip"]
+ )
+
+ if expected_nexthop["ipv4"] in rib_ipv4_nxt_hops:
+ is_ipv4_default_route_found = True
+ logger.info(
+ "{} default route with next hop {} is found in FIB ".format(
+ ipv4_default_route, expected_nexthop
+ )
+ )
+ else:
+ logger.error(
+ "ERROR .. ! {} default route with next hop {} is not found in FIB ".format(
+ ipv4_default_route, expected_nexthop
+ )
+ )
+ return False
+
+ if routes["ipv6"] in ipv6_routes.keys() or "::/0" in ipv6_routes.keys():
+ rib_ipv6_nxt_hops = []
+ if "::/0" in ipv6_routes.keys():
+ ipv6_default_route = "::/0"
+ elif routes["ipv6"] in ipv6_routes.keys():
+ ipv6_default_route = routes["ipv6"]
+
+ nxt_hop_count = len(ipv6_routes[ipv6_default_route][0]["nexthops"])
+ for index in range(nxt_hop_count):
+ rib_ipv6_nxt_hops.append(
+ ipv6_routes[ipv6_default_route][0]["nexthops"][index]["ip"]
+ )
+
+ if expected_nexthop["ipv6"] in rib_ipv6_nxt_hops:
+ is_ipv6_default_route_found = True
+ logger.info(
+ "{} default route with next hop {} is found in FIB ".format(
+ ipv6_default_route, expected_nexthop
+ )
+ )
+ else:
+ logger.error(
+ "ERROR .. ! {} default route with next hop {} is not found in FIB ".format(
+ ipv6_default_route, expected_nexthop
+ )
+ )
+ return False
+
+ if is_ipv4_default_route_found and is_ipv6_default_route_found:
+ return True
+ else:
+ logger.error(
+ "Default Route for ipv4 and ipv6 address family is not found in FIB "
+ )
+ return False
+
+
+@retry(retry_timeout=5)
+def verify_bgp_advertised_routes_from_neighbor(tgen, topo, dut, peer, expected_routes):
+ """
+ APi is verifies the the routes that are advertised from dut to peer
+
+ command used :
+ "sh ip bgp neighbor <x.x.x.x> advertised-routes" and
+ "sh ip bgp ipv6 unicast neighbor<x::x> advertised-routes"
+
+ dut : Device Under Tests
+ Peer : Peer on which the routs is expected
+ expected_routes : dual stack IPV4-and IPv6 routes to be verified
+ expected_routes
+
+ returns: True / False
+
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+
+ peer_ipv4_neighbor_ip = topo["routers"][peer]["links"][dut]["ipv4"].split("/")[0]
+ peer_ipv6_neighbor_ip = topo["routers"][peer]["links"][dut]["ipv6"].split("/")[0]
+
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+ ipv4_receieved_routes = run_frr_cmd(
+ rnode,
+ "sh ip bgp neighbor {} advertised-routes json".format(
+ peer_ipv4_neighbor_ip
+ ),
+ isjson=True,
+ )
+ ipv6_receieved_routes = run_frr_cmd(
+ rnode,
+ "sh ip bgp ipv6 unicast neighbor {} advertised-routes json".format(
+ peer_ipv6_neighbor_ip
+ ),
+ isjson=True,
+ )
+ ipv4_route_count = 0
+ ipv6_route_count = 0
+ if ipv4_receieved_routes:
+ for index in range(len(expected_routes["ipv4"])):
+ if (
+ expected_routes["ipv4"][index]["network"]
+ in ipv4_receieved_routes["advertisedRoutes"].keys()
+ ):
+ ipv4_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is advertised to {} ".format(
+ dut, expected_routes["ipv4"][index]["network"], peer
+ )
+ )
+
+ elif (
+ expected_routes["ipv4"][index]["network"]
+ in ipv4_receieved_routes["bgpOriginatingDefaultNetwork"]
+ ):
+ ipv4_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is advertised to {} ".format(
+ dut, expected_routes["ipv4"][index]["network"], peer
+ )
+ )
+
+ else:
+ logger.error(
+ "ERROR....![DUT : {}] The Expected Route {} is not advertised to {} ".format(
+ dut, expected_routes["ipv4"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(ipv4_receieved_routes)
+ logger.error(
+ "ERROR...! [DUT : {}] No IPV4 Routes are advertised to the peer {}".format(
+ dut, peer
+ )
+ )
+ return False
+
+ if ipv6_receieved_routes:
+ for index in range(len(expected_routes["ipv6"])):
+ if (
+ expected_routes["ipv6"][index]["network"]
+ in ipv6_receieved_routes["advertisedRoutes"].keys()
+ ):
+ ipv6_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is advertised to {} ".format(
+ dut, expected_routes["ipv6"][index]["network"], peer
+ )
+ )
+ elif (
+ expected_routes["ipv6"][index]["network"]
+ in ipv6_receieved_routes["bgpOriginatingDefaultNetwork"]
+ ):
+ ipv6_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is advertised to {} ".format(
+ dut, expected_routes["ipv6"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(
+ "ERROR....![DUT : {}] The Expected Route {} is not advertised to {} ".format(
+ dut, expected_routes["ipv6"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(ipv6_receieved_routes)
+ logger.error(
+ "ERROR...! [DUT : {}] No IPV6 Routes are advertised to the peer {}".format(
+ dut, peer
+ )
+ )
+ return False
+
+ if ipv4_route_count == len(expected_routes["ipv4"]) and ipv6_route_count == len(
+ expected_routes["ipv6"]
+ ):
+ return True
+ else:
+ logger.error(
+ "ERROR ....! IPV4 : Expected Routes -> {} obtained ->{} ".format(
+ expected_routes["ipv4"], ipv4_receieved_routes["advertisedRoutes"]
+ )
+ )
+ logger.error(
+ "ERROR ....! IPV6 : Expected Routes -> {} obtained ->{} ".format(
+ expected_routes["ipv6"], ipv6_receieved_routes["advertisedRoutes"]
+ )
+ )
+ return False
+
+
+@retry(retry_timeout=5)
+def verify_bgp_received_routes_from_neighbor(tgen, topo, dut, peer, expected_routes):
+ """
+ API to verify the bgp received routes
+
+ commad used :
+ =============
+ show ip bgp neighbor <x.x.x.x> received-routes
+ show ip bgp ipv6 unicast neighbor <x::x> received-routes
+
+ params
+ =======
+ dut : Device Under Tests
+ Peer : Peer on which the routs is expected
+ expected_routes : dual stack IPV4-and IPv6 routes to be verified
+ expected_routes
+
+ returns:
+ ========
+ True / False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ tgen = get_topogen()
+
+ peer_ipv4_neighbor_ip = topo["routers"][peer]["links"][dut]["ipv4"].split("/")[0]
+ peer_ipv6_neighbor_ip = topo["routers"][peer]["links"][dut]["ipv6"].split("/")[0]
+
+ logger.info("Enabling Soft configuration to neighbor INBOUND ")
+ neigbor_dict = {"ipv4": peer_ipv4_neighbor_ip, "ipv6": peer_ipv6_neighbor_ip}
+ result = configure_bgp_soft_configuration(
+ tgen, dut, neigbor_dict, direction="inbound"
+ )
+ assert (
+ result is True
+ ), " Failed to configure the soft configuration \n Error: {}".format(result)
+
+ """sleep of 10 sec is required to get the routes on peer after soft configuration"""
+ sleep(10)
+ for router, rnode in tgen.routers().items():
+ if router == dut:
+ ipv4_receieved_routes = run_frr_cmd(
+ rnode,
+ "sh ip bgp neighbor {} received-routes json".format(
+ peer_ipv4_neighbor_ip
+ ),
+ isjson=True,
+ )
+ ipv6_receieved_routes = run_frr_cmd(
+ rnode,
+ "sh ip bgp ipv6 unicast neighbor {} received-routes json".format(
+ peer_ipv6_neighbor_ip
+ ),
+ isjson=True,
+ )
+ ipv4_route_count = 0
+ ipv6_route_count = 0
+ if ipv4_receieved_routes:
+ for index in range(len(expected_routes["ipv4"])):
+ if (
+ expected_routes["ipv4"][index]["network"]
+ in ipv4_receieved_routes["receivedRoutes"].keys()
+ ):
+ ipv4_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is received from {} ".format(
+ dut, expected_routes["ipv4"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(
+ "ERROR....![DUT : {}] The Expected Route {} is not received from {} ".format(
+ dut, expected_routes["ipv4"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(ipv4_receieved_routes)
+ logger.error(
+ "ERROR...! [DUT : {}] No IPV4 Routes are received from the peer {}".format(
+ dut, peer
+ )
+ )
+ return False
+
+ if ipv6_receieved_routes:
+ for index in range(len(expected_routes["ipv6"])):
+ if (
+ expected_routes["ipv6"][index]["network"]
+ in ipv6_receieved_routes["receivedRoutes"].keys()
+ ):
+ ipv6_route_count += 1
+ logger.info(
+ "Success [DUT : {}] The Expected Route {} is received from {} ".format(
+ dut, expected_routes["ipv6"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(
+ "ERROR....![DUT : {}] The Expected Route {} is not received from {} ".format(
+ dut, expected_routes["ipv6"][index]["network"], peer
+ )
+ )
+ else:
+ logger.error(ipv6_receieved_routes)
+ logger.error(
+ "ERROR...! [DUT : {}] No IPV6 Routes are received from the peer {}".format(
+ dut, peer
+ )
+ )
+ return False
+
+ if ipv4_route_count == len(expected_routes["ipv4"]) and ipv6_route_count == len(
+ expected_routes["ipv6"]
+ ):
+ return True
+ else:
+ logger.error(
+ "ERROR ....! IPV4 : Expected Routes -> {} obtained ->{} ".format(
+ expected_routes["ipv4"], ipv4_receieved_routes["advertisedRoutes"]
+ )
+ )
+ logger.error(
+ "ERROR ....! IPV6 : Expected Routes -> {} obtained ->{} ".format(
+ expected_routes["ipv6"], ipv6_receieved_routes["advertisedRoutes"]
+ )
+ )
+ return False
+
+
+def configure_bgp_soft_configuration(tgen, dut, neighbor_dict, direction):
+ """
+ Api to configure the bgp soft configuration to show the received routes from peer
+ params
+ ======
+ dut : device under test route on which the sonfiguration to be applied
+ neighbor_dict : dict element contains ipv4 and ipv6 neigbor ip
+ direction : Directionon which it should be applied in/out
+
+ returns:
+ ========
+ boolean
+ """
+ logger.info("Enabling Soft configuration to neighbor INBOUND ")
+ local_as = get_dut_as_number(tgen, dut)
+ ipv4_neighbor = neighbor_dict["ipv4"]
+ ipv6_neighbor = neighbor_dict["ipv6"]
+ direction = direction.lower()
+ if ipv4_neighbor and ipv4_neighbor:
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "router bgp {}".format(local_as),
+ "address-family ipv4 unicast",
+ "neighbor {} soft-reconfiguration {} ".format(
+ ipv4_neighbor, direction
+ ),
+ "exit-address-family",
+ "address-family ipv6 unicast",
+ "neighbor {} soft-reconfiguration {} ".format(
+ ipv6_neighbor, direction
+ ),
+ "exit-address-family",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ logger.info(
+ "Success... [DUT : {}] The soft configuration onis applied on neighbors {} ".format(
+ dut, neighbor_dict
+ )
+ )
+ return True
diff --git a/tests/topotests/lib/bgprib.py b/tests/topotests/lib/bgprib.py
new file mode 100644
index 0000000..35a57d0
--- /dev/null
+++ b/tests/topotests/lib/bgprib.py
@@ -0,0 +1,177 @@
+#!/usr/bin/env python
+
+# Copyright 2018, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+#
+# want_rd_routes = [
+# {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1', 'bp': True},
+# {'rd':'10:1', 'p':'5.1.0.0/24', 'n':'1.1.1.1', 'bp': False},
+#
+# {'rd':'10:3', 'p':'5.1.0.0/24', 'n':'3.3.3.3'},
+# ]
+#
+# ribRequireVpnRoutes('r2','Customer routes',want_rd_routes)
+#
+# want_unicast_routes = [
+# {'p':'5.1.0.0/24', 'n':'1.1.1.1'},
+# ]
+#
+# ribRequireUnicastRoutes('r1','ipv4','r1-cust1','Customer routes in vrf',want_unicast_routes)
+# ribRequireUnicastRoutes('r1','ipv4','','Customer routes in default',want_unicast_routes)
+#
+
+from lib.lutil import luCommand, luResult, LUtil
+import json
+import re
+
+# gpz: get rib in json form and compare against desired routes
+class BgpRib:
+ def log(self, str):
+ LUtil.log("BgpRib: " + str)
+
+ def routes_include_wanted(self, pfxtbl, want, debug):
+ # helper function to RequireVpnRoutes
+ for pfx in pfxtbl.keys():
+ if debug:
+ self.log("trying pfx %s" % pfx)
+ if pfx != want["p"]:
+ if debug:
+ self.log("want pfx=" + want["p"] + ", not " + pfx)
+ continue
+ if debug:
+ self.log("have pfx=%s" % pfx)
+ for r in pfxtbl[pfx]:
+ bp = r.get("bestpath", False)
+ if debug:
+ self.log("trying route %s bp=%s" % (r, bp))
+ nexthops = r["nexthops"]
+ for nh in nexthops:
+ if debug:
+ self.log("trying nh %s" % nh["ip"])
+ if nh["ip"] == want["n"]:
+ if debug:
+ self.log("found %s" % want["n"])
+ if bp == want.get("bp", bp):
+ return 1
+ elif debug:
+ self.log("bestpath mismatch %s != %s" % (bp, want["bp"]))
+ else:
+ if debug:
+ self.log("want nh=" + want["n"] + ", not " + nh["ip"])
+ if debug:
+ self.log("missing route: pfx=" + want["p"] + ", nh=" + want["n"])
+ return 0
+
+ def RequireVpnRoutes(self, target, title, wantroutes, debug=0):
+ import json
+
+ logstr = "RequireVpnRoutes " + str(wantroutes)
+ # non json form for humans
+ luCommand(
+ target,
+ 'vtysh -c "show bgp ipv4 vpn"',
+ ".",
+ "None",
+ "Get VPN RIB (non-json)",
+ )
+ ret = luCommand(
+ target,
+ 'vtysh -c "show bgp ipv4 vpn json"',
+ ".*",
+ "None",
+ "Get VPN RIB (json)",
+ )
+ if re.search(r"^\s*$", ret):
+ # degenerate case: empty json means no routes
+ if len(wantroutes) > 0:
+ luResult(target, False, title, logstr)
+ return
+ luResult(target, True, title, logstr)
+ rib = json.loads(ret)
+ rds = rib["routes"]["routeDistinguishers"]
+ for want in wantroutes:
+ found = 0
+ if debug:
+ self.log("want rd %s" % want["rd"])
+ for rd in rds.keys():
+ if rd != want["rd"]:
+ continue
+ if debug:
+ self.log("found rd %s" % rd)
+ table = rds[rd]
+ if self.routes_include_wanted(table, want, debug):
+ found = 1
+ break
+ if not found:
+ luResult(target, False, title, logstr)
+ return
+ luResult(target, True, title, logstr)
+
+ def RequireUnicastRoutes(self, target, afi, vrf, title, wantroutes, debug=0):
+ logstr = "RequireUnicastRoutes %s" % str(wantroutes)
+ vrfstr = ""
+ if vrf != "":
+ vrfstr = "vrf %s" % (vrf)
+
+ if (afi != "ipv4") and (afi != "ipv6"):
+ self.log("ERROR invalid afi")
+
+ cmdstr = "show bgp %s %s unicast" % (vrfstr, afi)
+ # non json form for humans
+ cmd = 'vtysh -c "%s"' % cmdstr
+ luCommand(target, cmd, ".", "None", "Get %s %s RIB (non-json)" % (vrfstr, afi))
+ cmd = 'vtysh -c "%s json"' % cmdstr
+ ret = luCommand(
+ target, cmd, ".*", "None", "Get %s %s RIB (json)" % (vrfstr, afi)
+ )
+ if re.search(r"^\s*$", ret):
+ # degenerate case: empty json means no routes
+ if len(wantroutes) > 0:
+ luResult(target, False, title, logstr)
+ return
+ luResult(target, True, title, logstr)
+ rib = json.loads(ret)
+ try:
+ table = rib["routes"]
+ # KeyError: 'routes' probably means missing/bad VRF
+ except KeyError as err:
+ if vrf != "":
+ errstr = "-script ERROR: check if wrong vrf (%s)" % (vrf)
+ else:
+ errstr = "-script ERROR: check if vrf missing"
+ luResult(target, False, title + errstr, logstr)
+ return
+ # if debug:
+ # self.log("table=%s" % table)
+ for want in wantroutes:
+ if debug:
+ self.log("want=%s" % want)
+ if not self.routes_include_wanted(table, want, debug):
+ luResult(target, False, title, logstr)
+ return
+ luResult(target, True, title, logstr)
+
+
+BgpRib = BgpRib()
+
+
+def bgpribRequireVpnRoutes(target, title, wantroutes, debug=0):
+ BgpRib.RequireVpnRoutes(target, title, wantroutes, debug)
+
+
+def bgpribRequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug=0):
+ BgpRib.RequireUnicastRoutes(target, afi, vrf, title, wantroutes, debug)
diff --git a/tests/topotests/lib/common_config.py b/tests/topotests/lib/common_config.py
new file mode 100644
index 0000000..5f4c280
--- /dev/null
+++ b/tests/topotests/lib/common_config.py
@@ -0,0 +1,5130 @@
+#
+# Copyright (c) 2019 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.
+#
+
+import ipaddress
+import json
+import os
+import platform
+import socket
+import subprocess
+import sys
+import traceback
+import functools
+from collections import OrderedDict
+from copy import deepcopy
+from datetime import datetime, timedelta
+from functools import wraps
+from re import search as re_search
+from time import sleep
+
+try:
+ # Imports from python2
+ import ConfigParser as configparser
+except ImportError:
+ # Imports from python3
+ import configparser
+
+from lib.micronet import comm_error
+from lib.topogen import TopoRouter, get_topogen
+from lib.topolog import get_logger, logger
+from lib.topotest import frr_unicode, interface_set_status, version_cmp
+from lib import topotest
+
+FRRCFG_FILE = "frr_json.conf"
+FRRCFG_BKUP_FILE = "frr_json_initial.conf"
+
+ERROR_LIST = ["Malformed", "Failure", "Unknown", "Incomplete"]
+
+####
+CD = os.path.dirname(os.path.realpath(__file__))
+PYTESTINI_PATH = os.path.join(CD, "../pytest.ini")
+
+# NOTE: to save execution logs to log file frrtest_log_dir must be configured
+# in `pytest.ini`.
+config = configparser.ConfigParser()
+config.read(PYTESTINI_PATH)
+
+config_section = "topogen"
+
+# Debug logs for daemons
+DEBUG_LOGS = {
+ "pimd": [
+ "debug msdp events",
+ "debug msdp packets",
+ "debug igmp events",
+ "debug igmp trace",
+ "debug mroute",
+ "debug mroute detail",
+ "debug pim events",
+ "debug pim packets",
+ "debug pim trace",
+ "debug pim zebra",
+ "debug pim bsm",
+ "debug pim packets joins",
+ "debug pim packets register",
+ "debug pim nht",
+ ],
+ "bgpd": [
+ "debug bgp neighbor-events",
+ "debug bgp updates",
+ "debug bgp zebra",
+ "debug bgp nht",
+ "debug bgp neighbor-events",
+ "debug bgp graceful-restart",
+ "debug bgp update-groups",
+ "debug bgp vpn leak-from-vrf",
+ "debug bgp vpn leak-to-vrf",
+ "debug bgp zebr",
+ "debug bgp updates",
+ "debug bgp nht",
+ "debug bgp neighbor-events",
+ "debug vrf",
+ ],
+ "zebra": [
+ "debug zebra events",
+ "debug zebra rib",
+ "debug zebra vxlan",
+ "debug zebra nht",
+ ],
+ "ospf": [
+ "debug ospf event",
+ "debug ospf ism",
+ "debug ospf lsa",
+ "debug ospf nsm",
+ "debug ospf nssa",
+ "debug ospf packet all",
+ "debug ospf sr",
+ "debug ospf te",
+ "debug ospf zebra",
+ ],
+ "ospf6": [
+ "debug ospf6 event",
+ "debug ospf6 ism",
+ "debug ospf6 lsa",
+ "debug ospf6 nsm",
+ "debug ospf6 nssa",
+ "debug ospf6 packet all",
+ "debug ospf6 sr",
+ "debug ospf6 te",
+ "debug ospf6 zebra",
+ ],
+}
+
+g_iperf_client_procs = {}
+g_iperf_server_procs = {}
+
+
+def is_string(value):
+ try:
+ return isinstance(value, basestring)
+ except NameError:
+ return isinstance(value, str)
+
+
+if config.has_option("topogen", "verbosity"):
+ loglevel = config.get("topogen", "verbosity")
+ loglevel = loglevel.lower()
+else:
+ loglevel = "info"
+
+if config.has_option("topogen", "frrtest_log_dir"):
+ frrtest_log_dir = config.get("topogen", "frrtest_log_dir")
+ time_stamp = datetime.time(datetime.now())
+ logfile_name = "frr_test_bgp_"
+ frrtest_log_file = frrtest_log_dir + logfile_name + str(time_stamp)
+ print("frrtest_log_file..", frrtest_log_file)
+
+ logger = get_logger(
+ "test_execution_logs", log_level=loglevel, target=frrtest_log_file
+ )
+ print("Logs will be sent to logfile: {}".format(frrtest_log_file))
+
+if config.has_option("topogen", "show_router_config"):
+ show_router_config = config.get("topogen", "show_router_config")
+else:
+ show_router_config = False
+
+# env variable for setting what address type to test
+ADDRESS_TYPES = os.environ.get("ADDRESS_TYPES")
+
+
+# Saves sequence id numbers
+SEQ_ID = {"prefix_lists": {}, "route_maps": {}}
+
+
+def get_seq_id(obj_type, router, obj_name):
+ """
+ Generates and saves sequence number in interval of 10
+ Parameters
+ ----------
+ * `obj_type`: prefix_lists or route_maps
+ * `router`: router name
+ *` obj_name`: name of the prefix-list or route-map
+ Returns
+ --------
+ Sequence number generated
+ """
+
+ router_data = SEQ_ID[obj_type].setdefault(router, {})
+ obj_data = router_data.setdefault(obj_name, {})
+ seq_id = obj_data.setdefault("seq_id", 0)
+
+ seq_id = int(seq_id) + 10
+ obj_data["seq_id"] = seq_id
+
+ return seq_id
+
+
+def set_seq_id(obj_type, router, id, obj_name):
+ """
+ Saves sequence number if not auto-generated and given by user
+ Parameters
+ ----------
+ * `obj_type`: prefix_lists or route_maps
+ * `router`: router name
+ *` obj_name`: name of the prefix-list or route-map
+ """
+ router_data = SEQ_ID[obj_type].setdefault(router, {})
+ obj_data = router_data.setdefault(obj_name, {})
+ seq_id = obj_data.setdefault("seq_id", 0)
+
+ seq_id = int(seq_id) + int(id)
+ obj_data["seq_id"] = seq_id
+
+
+class InvalidCLIError(Exception):
+ """Raise when the CLI command is wrong"""
+
+
+def run_frr_cmd(rnode, cmd, isjson=False):
+ """
+ Execute frr show commands in privileged mode
+ * `rnode`: router node on which command needs to be executed
+ * `cmd`: Command to be executed on frr
+ * `isjson`: If command is to get json data or not
+ :return str:
+ """
+
+ if cmd:
+ ret_data = rnode.vtysh_cmd(cmd, isjson=isjson)
+
+ if isjson:
+ rnode.vtysh_cmd(cmd.rstrip("json"), isjson=False)
+
+ return ret_data
+
+ else:
+ raise InvalidCLIError("No actual cmd passed")
+
+
+def apply_raw_config(tgen, input_dict):
+
+ """
+ API to configure raw configuration on device. This can be used for any cli
+ which has not been implemented in JSON.
+
+ Parameters
+ ----------
+ * `tgen`: tgen object
+ * `input_dict`: configuration that needs to be applied
+
+ Usage
+ -----
+ input_dict = {
+ "r2": {
+ "raw_config": [
+ "router bgp",
+ "no bgp update-group-split-horizon"
+ ]
+ }
+ }
+ Returns
+ -------
+ True or errormsg
+ """
+
+ rlist = []
+
+ for router_name in input_dict.keys():
+ config_cmd = input_dict[router_name]["raw_config"]
+
+ if not isinstance(config_cmd, list):
+ config_cmd = [config_cmd]
+
+ frr_cfg_file = "{}/{}/{}".format(tgen.logdir, router_name, FRRCFG_FILE)
+ with open(frr_cfg_file, "w") as cfg:
+ for cmd in config_cmd:
+ cfg.write("{}\n".format(cmd))
+
+ rlist.append(router_name)
+
+ # Load config on all routers
+ return load_config_to_routers(tgen, rlist)
+
+
+def create_common_configurations(
+ tgen, config_dict, config_type=None, build=False, load_config=True
+):
+ """
+ API to create object of class FRRConfig and also create frr_json.conf
+ file. It will create interface and common configurations and save it to
+ frr_json.conf and load to router
+ Parameters
+ ----------
+ * `tgen`: tgen object
+ * `config_dict`: Configuration data saved in a dict of { router: config-list }
+ * `routers` : list of router id to be configured.
+ * `config_type` : Syntactic information while writing configuration. Should
+ be one of the value as mentioned in the config_map below.
+ * `build` : Only for initial setup phase this is set as True
+ Returns
+ -------
+ True or False
+ """
+
+ config_map = OrderedDict(
+ {
+ "general_config": "! FRR General Config\n",
+ "debug_log_config": "! Debug log Config\n",
+ "interface_config": "! Interfaces Config\n",
+ "static_route": "! Static Route Config\n",
+ "prefix_list": "! Prefix List Config\n",
+ "bgp_community_list": "! Community List Config\n",
+ "route_maps": "! Route Maps Config\n",
+ "bgp": "! BGP Config\n",
+ "vrf": "! VRF Config\n",
+ "ospf": "! OSPF Config\n",
+ "ospf6": "! OSPF Config\n",
+ "pim": "! PIM Config\n",
+ }
+ )
+
+ if build:
+ mode = "a"
+ elif not load_config:
+ mode = "a"
+ else:
+ mode = "w"
+
+ routers = config_dict.keys()
+ for router in routers:
+ fname = "{}/{}/{}".format(tgen.logdir, router, FRRCFG_FILE)
+ try:
+ frr_cfg_fd = open(fname, mode)
+ if config_type:
+ frr_cfg_fd.write(config_map[config_type])
+ for line in config_dict[router]:
+ frr_cfg_fd.write("{} \n".format(str(line)))
+ frr_cfg_fd.write("\n")
+
+ except IOError as err:
+ logger.error("Unable to open FRR Config '%s': %s" % (fname, str(err)))
+ return False
+ finally:
+ frr_cfg_fd.close()
+
+ # If configuration applied from build, it will done at last
+ result = True
+ if not build and load_config:
+ result = load_config_to_routers(tgen, routers)
+
+ return result
+
+
+def create_common_configuration(
+ tgen, router, data, config_type=None, build=False, load_config=True
+):
+ """
+ API to create object of class FRRConfig and also create frr_json.conf
+ file. It will create interface and common configurations and save it to
+ frr_json.conf and load to router
+ Parameters
+ ----------
+ * `tgen`: tgen object
+ * `data`: Configuration data saved in a list.
+ * `router` : router id to be configured.
+ * `config_type` : Syntactic information while writing configuration. Should
+ be one of the value as mentioned in the config_map below.
+ * `build` : Only for initial setup phase this is set as True
+ Returns
+ -------
+ True or False
+ """
+ return create_common_configurations(
+ tgen, {router: data}, config_type, build, load_config
+ )
+
+
+def kill_router_daemons(tgen, router, daemons, save_config=True):
+ """
+ Router's current config would be saved to /etc/frr/ for each daemon
+ and daemon would be killed forcefully using SIGKILL.
+ * `tgen` : topogen object
+ * `router`: Device under test
+ * `daemons`: list of daemons to be killed
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ router_list = tgen.routers()
+
+ if save_config:
+ # Saving router config to /etc/frr, which will be loaded to router
+ # when it starts
+ router_list[router].vtysh_cmd("write memory")
+
+ # Kill Daemons
+ result = router_list[router].killDaemons(daemons)
+ if len(result) > 0:
+ assert "Errors found post shutdown - details follow:" == 0, result
+ return result
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+
+def start_router_daemons(tgen, router, daemons):
+ """
+ Daemons defined by user would be started
+ * `tgen` : topogen object
+ * `router`: Device under test
+ * `daemons`: list of daemons to be killed
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ router_list = tgen.routers()
+
+ # Start daemons
+ res = router_list[router].startDaemons(daemons)
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ res = errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return res
+
+
+def check_router_status(tgen):
+ """
+ Check if all daemons are running for all routers in topology
+ * `tgen` : topogen object
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+
+ result = rnode.check_router_running()
+ if result != "":
+ daemons = []
+ if "bgpd" in result:
+ daemons.append("bgpd")
+ if "zebra" in result:
+ daemons.append("zebra")
+ if "pimd" in result:
+ daemons.append("pimd")
+ if "pim6d" in result:
+ daemons.append("pim6d")
+ if "ospfd" in result:
+ daemons.append("ospfd")
+ if "ospf6d" in result:
+ daemons.append("ospf6d")
+ rnode.startDaemons(daemons)
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def save_initial_config_on_routers(tgen):
+ """Save current configuration on routers to FRRCFG_BKUP_FILE.
+
+ FRRCFG_BKUP_FILE is the file that will be restored when `reset_config_on_routers()`
+ is called.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ """
+ router_list = tgen.routers()
+ target_cfg_fmt = tgen.logdir + "/{}/frr_json_initial.conf"
+
+ # Get all running configs in parallel
+ procs = {}
+ for rname in router_list:
+ logger.info("Fetching running config for router %s", rname)
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
+ stdin=None,
+ stdout=open(target_cfg_fmt.format(rname), "w"),
+ stderr=subprocess.PIPE,
+ )
+ for rname, p in procs.items():
+ _, error = p.communicate()
+ if p.returncode:
+ logger.error(
+ "Get running config for %s failed %d: %s", rname, p.returncode, error
+ )
+ raise InvalidCLIError(
+ "vtysh show running error on {}: {}".format(rname, error)
+ )
+
+
+def reset_config_on_routers(tgen, routerName=None):
+ """
+ Resets configuration on routers to the snapshot created using input JSON
+ file. It replaces existing router configuration with FRRCFG_BKUP_FILE
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `routerName` : router config is to be reset
+ """
+
+ logger.debug("Entering API: reset_config_on_routers")
+
+ tgen.cfg_gen += 1
+ gen = tgen.cfg_gen
+
+ # Trim the router list if needed
+ router_list = tgen.routers()
+ if routerName:
+ if routerName not in router_list:
+ logger.warning(
+ "Exiting API: reset_config_on_routers: no router %s",
+ routerName,
+ exc_info=True,
+ )
+ return True
+ router_list = {routerName: router_list[routerName]}
+
+ delta_fmt = tgen.logdir + "/{}/delta-{}.conf"
+ # FRRCFG_BKUP_FILE
+ target_cfg_fmt = tgen.logdir + "/{}/frr_json_initial.conf"
+ run_cfg_fmt = tgen.logdir + "/{}/frr-{}.sav"
+
+ #
+ # Get all running configs in parallel
+ #
+ procs = {}
+ for rname in router_list:
+ logger.info("Fetching running config for router %s", rname)
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
+ stdin=None,
+ stdout=open(run_cfg_fmt.format(rname, gen), "w"),
+ stderr=subprocess.PIPE,
+ )
+ for rname, p in procs.items():
+ _, error = p.communicate()
+ if p.returncode:
+ logger.error(
+ "Get running config for %s failed %d: %s", rname, p.returncode, error
+ )
+ raise InvalidCLIError(
+ "vtysh show running error on {}: {}".format(rname, error)
+ )
+
+ #
+ # Get all delta's in parallel
+ #
+ procs = {}
+ for rname in router_list:
+ logger.info(
+ "Generating delta for router %s to new configuration (gen %d)", rname, gen
+ )
+ procs[rname] = tgen.net.popen(
+ [
+ "/usr/lib/frr/frr-reload.py",
+ "--test-reset",
+ "--input",
+ run_cfg_fmt.format(rname, gen),
+ "--test",
+ target_cfg_fmt.format(rname),
+ ],
+ stdin=None,
+ stdout=open(delta_fmt.format(rname, gen), "w"),
+ stderr=subprocess.PIPE,
+ )
+ for rname, p in procs.items():
+ _, error = p.communicate()
+ if p.returncode:
+ logger.error(
+ "Delta file creation for %s failed %d: %s", rname, p.returncode, error
+ )
+ raise InvalidCLIError("frr-reload error for {}: {}".format(rname, error))
+
+ #
+ # Apply all the deltas in parallel
+ #
+ procs = {}
+ for rname in router_list:
+ logger.info("Applying delta config on router %s", rname)
+
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-f", delta_fmt.format(rname, gen)],
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ for rname, p in procs.items():
+ output, _ = p.communicate()
+ vtysh_command = "vtysh -f {}".format(delta_fmt.format(rname, gen))
+ if not p.returncode:
+ router_list[rname].logger.info(
+ '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ else:
+ router_list[rname].logger.warning(
+ '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ logger.error(
+ "Delta file apply for %s failed %d: %s", rname, p.returncode, output
+ )
+
+ # We really need to enable this failure; however, currently frr-reload.py
+ # producing invalid "no" commands as it just preprends "no", but some of the
+ # command forms lack matching values (e.g., final values). Until frr-reload
+ # is fixed to handle this (or all the CLI no forms are adjusted) we can't
+ # fail tests.
+ # raise InvalidCLIError("frr-reload error for {}: {}".format(rname, output))
+
+ #
+ # Optionally log all new running config if "show_router_config" is defined in
+ # "pytest.ini"
+ #
+ if show_router_config:
+ procs = {}
+ for rname in router_list:
+ logger.info("Fetching running config for router %s", rname)
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ for rname, p in procs.items():
+ output, _ = p.communicate()
+ if p.returncode:
+ logger.warning(
+ "Get running config for %s failed %d: %s",
+ rname,
+ p.returncode,
+ output,
+ )
+ else:
+ logger.info(
+ "Configuration on router %s after reset:\n%s", rname, output
+ )
+
+ logger.debug("Exiting API: reset_config_on_routers")
+ return True
+
+
+def prep_load_config_to_routers(tgen, *config_name_list):
+ """Create common config for `load_config_to_routers`.
+
+ The common config file is constructed from the list of sub-config files passed as
+ position arguments to this function. Each entry in `config_name_list` is looked for
+ under the router sub-directory in the test directory and those files are
+ concatenated together to create the common config. e.g.,
+
+ # Routers are "r1" and "r2", test file is `example/test_example_foo.py`
+ prepare_load_config_to_routers(tgen, "bgpd.conf", "ospfd.conf")
+
+ When the above call is made the files in
+
+ example/r1/bgpd.conf
+ example/r1/ospfd.conf
+
+ Are concat'd together into a single config file that will be loaded on r1, and
+
+ example/r2/bgpd.conf
+ example/r2/ospfd.conf
+
+ Are concat'd together into a single config file that will be loaded on r2 when
+ the call to `load_config_to_routers` is made.
+ """
+
+ routers = tgen.routers()
+ for rname, router in routers.items():
+ destname = "{}/{}/{}".format(tgen.logdir, rname, FRRCFG_FILE)
+ wmode = "w"
+ for cfbase in config_name_list:
+ script_dir = os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]
+ confname = os.path.join(script_dir, "{}/{}".format(rname, cfbase))
+ with open(confname, "r") as cf:
+ with open(destname, wmode) as df:
+ df.write(cf.read())
+ wmode = "a"
+
+
+def load_config_to_routers(tgen, routers, save_bkup=False):
+ """
+ Loads configuration on routers from the file FRRCFG_FILE.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `routers` : routers for which configuration is to be loaded
+ * `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE
+ Returns
+ -------
+ True or False
+ """
+
+ logger.debug("Entering API: load_config_to_routers")
+
+ tgen.cfg_gen += 1
+ gen = tgen.cfg_gen
+
+ base_router_list = tgen.routers()
+ router_list = {}
+ for router in routers:
+ if router not in base_router_list:
+ continue
+ router_list[router] = base_router_list[router]
+
+ frr_cfg_file_fmt = tgen.logdir + "/{}/" + FRRCFG_FILE
+ frr_cfg_save_file_fmt = tgen.logdir + "/{}/{}-" + FRRCFG_FILE
+ frr_cfg_bkup_fmt = tgen.logdir + "/{}/" + FRRCFG_BKUP_FILE
+
+ procs = {}
+ for rname in router_list:
+ router = router_list[rname]
+ try:
+ frr_cfg_file = frr_cfg_file_fmt.format(rname)
+ frr_cfg_save_file = frr_cfg_save_file_fmt.format(rname, gen)
+ frr_cfg_bkup = frr_cfg_bkup_fmt.format(rname)
+ with open(frr_cfg_file, "r+") as cfg:
+ data = cfg.read()
+ logger.info(
+ "Applying following configuration on router %s (gen: %d):\n%s",
+ rname,
+ gen,
+ data,
+ )
+ # Always save a copy of what we just did
+ with open(frr_cfg_save_file, "w") as bkup:
+ bkup.write(data)
+ if save_bkup:
+ with open(frr_cfg_bkup, "w") as bkup:
+ bkup.write(data)
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-f", frr_cfg_file],
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ except IOError as err:
+ logger.error(
+ "Unable to open config File. error(%s): %s", err.errno, err.strerror
+ )
+ return False
+ except Exception as error:
+ logger.error("Unable to apply config on %s: %s", rname, str(error))
+ return False
+
+ errors = []
+ for rname, p in procs.items():
+ output, _ = p.communicate()
+ frr_cfg_file = frr_cfg_file_fmt.format(rname)
+ vtysh_command = "vtysh -f " + frr_cfg_file
+ if not p.returncode:
+ router_list[rname].logger.info(
+ '\nvtysh config apply => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ else:
+ router_list[rname].logger.error(
+ '\nvtysh config apply failed => "{}"\nvtysh output <= "{}"'.format(
+ vtysh_command, output
+ )
+ )
+ logger.error(
+ "Config apply for %s failed %d: %s", rname, p.returncode, output
+ )
+ # We can't thorw an exception here as we won't clear the config file.
+ errors.append(
+ InvalidCLIError(
+ "load_config_to_routers error for {}: {}".format(rname, output)
+ )
+ )
+
+ # Empty the config file or we append to it next time through.
+ with open(frr_cfg_file, "r+") as cfg:
+ cfg.truncate(0)
+
+ # Router current configuration to log file or console if
+ # "show_router_config" is defined in "pytest.ini"
+ if show_router_config:
+ procs = {}
+ for rname in router_list:
+ procs[rname] = router_list[rname].popen(
+ ["/usr/bin/env", "vtysh", "-c", "show running-config no-header"],
+ stdin=None,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ for rname, p in procs.items():
+ output, _ = p.communicate()
+ if p.returncode:
+ logger.warning(
+ "Get running config for %s failed %d: %s",
+ rname,
+ p.returncode,
+ output,
+ )
+ else:
+ logger.info("New configuration for router %s:\n%s", rname, output)
+
+ logger.debug("Exiting API: load_config_to_routers")
+ return not errors
+
+
+def load_config_to_router(tgen, routerName, save_bkup=False):
+ """
+ Loads configuration on router from the file FRRCFG_FILE.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `routerName` : router for which configuration to be loaded
+ * `save_bkup` : If True, Saves snapshot of FRRCFG_FILE to FRRCFG_BKUP_FILE
+ """
+ return load_config_to_routers(tgen, [routerName], save_bkup)
+
+
+def reset_with_new_configs(tgen, *cflist):
+ """Reset the router to initial config, then load new configs.
+
+ Resets routers to the initial config state (see `save_initial_config_on_routers()
+ and `reset_config_on_routers()` `), then concat list of router sub-configs together
+ and load onto the routers (see `prep_load_config_to_routers()` and
+ `load_config_to_routers()`)
+ """
+ routers = tgen.routers()
+
+ reset_config_on_routers(tgen)
+ prep_load_config_to_routers(tgen, *cflist)
+ load_config_to_routers(tgen, tgen.routers(), save_bkup=False)
+
+
+def get_frr_ipv6_linklocal(tgen, router, intf=None, vrf=None):
+ """
+ API to get the link local ipv6 address of a particular interface using
+ FRR command 'show interface'
+
+ * `tgen`: tgen object
+ * `router` : router for which highest interface should be
+ calculated
+ * `intf` : interface for which link-local address needs to be taken
+ * `vrf` : VRF name
+
+ Usage
+ -----
+ linklocal = get_frr_ipv6_linklocal(tgen, router, "intf1", RED_A)
+
+ Returns
+ -------
+ 1) array of interface names to link local ips.
+ """
+
+ router_list = tgen.routers()
+ for rname, rnode in router_list.items():
+ if rname != router:
+ continue
+
+ linklocal = []
+
+ if vrf:
+ cmd = "show interface vrf {}".format(vrf)
+ else:
+ cmd = "show interface"
+
+ linklocal = []
+ if vrf:
+ cmd = "show interface vrf {}".format(vrf)
+ else:
+ cmd = "show interface"
+ for chk_ll in range(0, 60):
+ sleep(1 / 4)
+ ifaces = router_list[router].run('vtysh -c "{}"'.format(cmd))
+ # Fix newlines (make them all the same)
+ ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
+
+ interface = None
+ ll_per_if_count = 0
+ for line in ifaces:
+ # Interface name
+ m = re_search("Interface ([a-zA-Z0-9-]+) is", line)
+ if m:
+ interface = m.group(1).split(" ")[0]
+ ll_per_if_count = 0
+
+ # Interface ip
+ m1 = re_search("inet6 (fe80[:a-fA-F0-9]+/[0-9]+)", line)
+ if m1:
+ local = m1.group(1)
+ ll_per_if_count += 1
+ if ll_per_if_count > 1:
+ linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
+ else:
+ linklocal += [[interface, local]]
+
+ try:
+ if linklocal:
+ if intf:
+ return [
+ _linklocal[1]
+ for _linklocal in linklocal
+ if _linklocal[0] == intf
+ ][0].split("/")[0]
+ return linklocal
+ except IndexError:
+ continue
+
+ errormsg = "Link local ip missing on router {}".format(router)
+ return errormsg
+
+
+def generate_support_bundle():
+ """
+ API to generate support bundle on any verification ste failure.
+ it runs a python utility, /usr/lib/frr/generate_support_bundle.py,
+ which basically runs defined CLIs and dumps the data to specified location
+ """
+
+ tgen = get_topogen()
+ router_list = tgen.routers()
+ test_name = os.environ.get("PYTEST_CURRENT_TEST").split(":")[-1].split(" ")[0]
+
+ bundle_procs = {}
+ for rname, rnode in router_list.items():
+ logger.info("Spawn collection of support bundle for %s", rname)
+ dst_bundle = "{}/{}/support_bundles/{}".format(tgen.logdir, rname, test_name)
+ rnode.run("mkdir -p " + dst_bundle)
+
+ gen_sup_cmd = [
+ "/usr/lib/frr/generate_support_bundle.py",
+ "--log-dir=" + dst_bundle,
+ ]
+ bundle_procs[rname] = tgen.net[rname].popen(gen_sup_cmd, stdin=None)
+
+ for rname, rnode in router_list.items():
+ logger.info("Waiting on support bundle for %s", rname)
+ output, error = bundle_procs[rname].communicate()
+ if output:
+ logger.info(
+ "Output from collecting support bundle for %s:\n%s", rname, output
+ )
+ if error:
+ logger.warning(
+ "Error from collecting support bundle for %s:\n%s", rname, error
+ )
+
+ return True
+
+
+def start_topology(tgen, daemon=None):
+ """
+ Starting topology, create tmp files which are loaded to routers
+ to start daemons and then start routers
+ * `tgen` : topogen object
+ """
+
+ # Starting topology
+ tgen.start_topology()
+
+ # Starting daemons
+
+ router_list = tgen.routers()
+ routers_sorted = sorted(
+ router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0))
+ )
+
+ linux_ver = ""
+ router_list = tgen.routers()
+ for rname in routers_sorted:
+ router = router_list[rname]
+
+ # It will help in debugging the failures, will give more details on which
+ # specific kernel version tests are failing
+ if linux_ver == "":
+ linux_ver = router.run("uname -a")
+ logger.info("Logging platform related details: \n %s \n", linux_ver)
+
+ try:
+ os.chdir(tgen.logdir)
+
+ # # Creating router named dir and empty zebra.conf bgpd.conf files
+ # # inside the current directory
+ # if os.path.isdir("{}".format(rname)):
+ # os.system("rm -rf {}".format(rname))
+ # os.mkdir("{}".format(rname))
+ # os.system("chmod -R go+rw {}".format(rname))
+ # os.chdir("{}/{}".format(tgen.logdir, rname))
+ # os.system("touch zebra.conf bgpd.conf")
+ # else:
+ # os.mkdir("{}".format(rname))
+ # os.system("chmod -R go+rw {}".format(rname))
+ # os.chdir("{}/{}".format(tgen.logdir, rname))
+ # os.system("touch zebra.conf bgpd.conf")
+
+ except IOError as err:
+ logger.error("I/O error({0}): {1}".format(err.errno, err.strerror))
+
+ # Loading empty zebra.conf file to router, to start the zebra daemon
+ router.load_config(
+ TopoRouter.RD_ZEBRA, "{}/{}/zebra.conf".format(tgen.logdir, rname)
+ )
+
+ # Loading empty bgpd.conf file to router, to start the bgp daemon
+ router.load_config(
+ TopoRouter.RD_BGP, "{}/{}/bgpd.conf".format(tgen.logdir, rname)
+ )
+
+ if daemon and "ospfd" in daemon:
+ # Loading empty ospf.conf file to router, to start the bgp daemon
+ router.load_config(
+ TopoRouter.RD_OSPF, "{}/{}/ospfd.conf".format(tgen.logdir, rname)
+ )
+
+ if daemon and "ospf6d" in daemon:
+ # Loading empty ospf.conf file to router, to start the bgp daemon
+ router.load_config(
+ TopoRouter.RD_OSPF6, "{}/{}/ospf6d.conf".format(tgen.logdir, rname)
+ )
+
+ if daemon and "pimd" in daemon:
+ # Loading empty pimd.conf file to router, to start the pim deamon
+ router.load_config(
+ TopoRouter.RD_PIM, "{}/{}/pimd.conf".format(tgen.logdir, rname)
+ )
+
+ if daemon and "pim6d" in daemon:
+ # Loading empty pimd.conf file to router, to start the pim6d deamon
+ router.load_config(
+ TopoRouter.RD_PIM6, "{}/{}/pim6d.conf".format(tgen.logdir, rname)
+ )
+
+ # Starting routers
+ logger.info("Starting all routers once topology is created")
+ tgen.start_router()
+
+
+def stop_router(tgen, router):
+ """
+ Router"s current config would be saved to /tmp/topotest/<suite>/<router> for each daemon
+ and router and its daemons would be stopped.
+
+ * `tgen` : topogen object
+ * `router`: Device under test
+ """
+
+ router_list = tgen.routers()
+
+ # Saving router config to /etc/frr, which will be loaded to router
+ # when it starts
+ router_list[router].vtysh_cmd("write memory")
+
+ # Stop router
+ router_list[router].stop()
+
+
+def start_router(tgen, router):
+ """
+ Router will be started and config would be loaded from /tmp/topotest/<suite>/<router> for each
+ daemon
+
+ * `tgen` : topogen object
+ * `router`: Device under test
+ """
+
+ logger.debug("Entering lib API: start_router")
+
+ try:
+ router_list = tgen.routers()
+
+ # Router and its daemons would be started and config would
+ # be loaded to router for each daemon from /etc/frr
+ router_list[router].start()
+
+ # Waiting for router to come up
+ sleep(5)
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: start_router()")
+ return True
+
+
+def number_to_row(routerName):
+ """
+ Returns the number for the router.
+ Calculation based on name a0 = row 0, a1 = row 1, b2 = row 2, z23 = row 23
+ etc
+ """
+ return int(routerName[1:])
+
+
+def number_to_column(routerName):
+ """
+ Returns the number for the router.
+ Calculation based on name a0 = columnn 0, a1 = column 0, b2= column 1,
+ z23 = column 26 etc
+ """
+ return ord(routerName[0]) - 97
+
+
+def topo_daemons(tgen, topo=None):
+ """
+ Returns daemon list required for the suite based on topojson.
+ """
+ daemon_list = []
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ router_list = tgen.routers()
+ routers_sorted = sorted(
+ router_list.keys(), key=lambda x: int(re_search("[0-9]+", x).group(0))
+ )
+
+ for rtr in routers_sorted:
+ if "ospf" in topo["routers"][rtr] and "ospfd" not in daemon_list:
+ daemon_list.append("ospfd")
+
+ if "ospf6" in topo["routers"][rtr] and "ospf6d" not in daemon_list:
+ daemon_list.append("ospf6d")
+
+ for val in topo["routers"][rtr]["links"].values():
+ if "pim" in val and "pimd" not in daemon_list:
+ daemon_list.append("pimd")
+ if "pim6" in val and "pim6d" not in daemon_list:
+ daemon_list.append("pim6d")
+ if "ospf" in val and "ospfd" not in daemon_list:
+ daemon_list.append("ospfd")
+ if "ospf6" in val and "ospf6d" not in daemon_list:
+ daemon_list.append("ospf6d")
+ break
+
+ return daemon_list
+
+
+def add_interfaces_to_vlan(tgen, input_dict):
+ """
+ Add interfaces to VLAN, we need vlan pakcage to be installed on machine
+
+ * `tgen`: tgen onject
+ * `input_dict` : interfaces to be added to vlans
+
+ input_dict= {
+ "r1":{
+ "vlan":{
+ VLAN_1: [{
+ intf_r1_s1: {
+ "ip": "10.1.1.1",
+ "subnet": "255.255.255.0
+ }
+ }]
+ }
+ }
+ }
+
+ add_interfaces_to_vlan(tgen, input_dict)
+
+ """
+
+ router_list = tgen.routers()
+ for dut in input_dict.keys():
+ rnode = router_list[dut]
+
+ if "vlan" in input_dict[dut]:
+ for vlan, interfaces in input_dict[dut]["vlan"].items():
+ for intf_dict in interfaces:
+ for interface, data in intf_dict.items():
+ # Adding interface to VLAN
+ vlan_intf = "{}.{}".format(interface, vlan)
+ cmd = "ip link add link {} name {} type vlan id {}".format(
+ interface, vlan_intf, vlan
+ )
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.info("result %s", result)
+
+ # Bringing interface up
+ cmd = "ip link set {} up".format(vlan_intf)
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.info("result %s", result)
+
+ # Assigning IP address
+ ifaddr = ipaddress.ip_interface(
+ "{}/{}".format(
+ frr_unicode(data["ip"]), frr_unicode(data["subnet"])
+ )
+ )
+
+ cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format(
+ ifaddr.version, vlan_intf, ifaddr
+ )
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ result = rnode.run(cmd)
+ logger.info("result %s", result)
+
+
+def tcpdump_capture_start(
+ tgen,
+ router,
+ intf,
+ protocol=None,
+ grepstr=None,
+ timeout=0,
+ options=None,
+ cap_file=None,
+ background=True,
+):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap_file` : filename to store capture dump.
+ * `background` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.gears[router]
+
+ if timeout > 0:
+ cmd = "timeout {}".format(timeout)
+ else:
+ cmd = ""
+
+ cmdargs = "{} tcpdump".format(cmd)
+
+ if intf:
+ cmdargs += " -i {}".format(str(intf))
+ if protocol:
+ cmdargs += " {}".format(str(protocol))
+ if options:
+ cmdargs += " -s 0 {}".format(str(options))
+
+ if cap_file:
+ file_name = os.path.join(tgen.logdir, router, cap_file)
+ cmdargs += " -w {}".format(str(file_name))
+ # Remove existing capture file
+ rnode.run("rm -rf {}".format(file_name))
+
+ if grepstr:
+ cmdargs += ' | grep "{}"'.format(str(grepstr))
+
+ logger.info("Running tcpdump command: [%s]", cmdargs)
+ if not background:
+ rnode.run(cmdargs)
+ else:
+ # XXX this & is bogus doesn't work
+ # rnode.run("nohup {} & /dev/null 2>&1".format(cmdargs))
+ rnode.run("nohup {} > /dev/null 2>&1".format(cmdargs))
+
+ # Check if tcpdump process is running
+ if background:
+ result = rnode.run("pgrep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not result:
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ logger.info("Packet capture started on %s: interface %s", router, intf)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def tcpdump_capture_stop(tgen, router):
+ """
+ API to capture network packets using tcp dump.
+
+ Packages used :
+
+ Parameters
+ ----------
+ * `tgen`: topogen object.
+ * `router`: router on which ping has to be performed.
+ * `intf` : interface for capture.
+ * `protocol` : protocol for which packet needs to be captured.
+ * `grepstr` : string to filter out tcp dump output.
+ * `timeout` : Time for which packet needs to be captured.
+ * `options` : options for TCP dump, all tcpdump options can be used.
+ * `cap2file` : filename to store capture dump.
+ * `bakgrnd` : Make tcp dump run in back ground.
+
+ Usage
+ -----
+ tcpdump_result = tcpdump_dut(tgen, 'r2', intf, protocol='tcp', timeout=20,
+ options='-A -vv -x > r2bgp.txt ')
+ Returns
+ -------
+ 1) True for successful capture
+ 2) errormsg - when tcp dump fails
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.gears[router]
+
+ # Check if tcpdump process is running
+ result = rnode.run("ps -ef | grep tcpdump")
+ logger.debug("ps -ef | grep tcpdump \n {}".format(result))
+
+ if not re_search(r"{}".format("tcpdump"), result):
+ errormsg = "tcpdump is not running {}".format("tcpdump")
+ return errormsg
+ else:
+ # XXX this doesn't work with micronet
+ ppid = tgen.net.nameToNode[rnode.name].pid
+ rnode.run("set +m; pkill -P %s tcpdump &> /dev/null" % ppid)
+ logger.info("Stopped tcpdump capture")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def create_debug_log_config(tgen, input_dict, build=False):
+ """
+ Enable/disable debug logs for any protocol with defined debug
+ options and logs would be saved to created log file
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : details to enable debug logs for protocols
+ * `build` : Only for initial setup phase this is set as True.
+
+
+ Usage:
+ ------
+ input_dict = {
+ "r2": {
+ "debug":{
+ "log_file" : "debug.log",
+ "enable": ["pimd", "zebra"],
+ "disable": {
+ "bgpd":[
+ 'debug bgp neighbor-events',
+ 'debug bgp updates',
+ 'debug bgp zebra',
+ ]
+ }
+ }
+ }
+ }
+
+ result = create_debug_log_config(tgen, input_dict)
+
+ Returns
+ -------
+ True or False
+ """
+
+ result = False
+ try:
+ debug_config_dict = {}
+
+ for router in input_dict.keys():
+ debug_config = []
+ if "debug" in input_dict[router]:
+ debug_dict = input_dict[router]["debug"]
+
+ disable_logs = debug_dict.setdefault("disable", None)
+ enable_logs = debug_dict.setdefault("enable", None)
+ log_file = debug_dict.setdefault("log_file", None)
+
+ if log_file:
+ _log_file = os.path.join(tgen.logdir, log_file)
+ debug_config.append("log file {} \n".format(_log_file))
+
+ if type(enable_logs) is list:
+ for daemon in enable_logs:
+ for debug_log in DEBUG_LOGS[daemon]:
+ debug_config.append("{}".format(debug_log))
+ elif type(enable_logs) is dict:
+ for daemon, debug_logs in enable_logs.items():
+ for debug_log in debug_logs:
+ debug_config.append("{}".format(debug_log))
+
+ if type(disable_logs) is list:
+ for daemon in disable_logs:
+ for debug_log in DEBUG_LOGS[daemon]:
+ debug_config.append("no {}".format(debug_log))
+ elif type(disable_logs) is dict:
+ for daemon, debug_logs in disable_logs.items():
+ for debug_log in debug_logs:
+ debug_config.append("no {}".format(debug_log))
+ if debug_config:
+ debug_config_dict[router] = debug_config
+
+ result = create_common_configurations(
+ tgen, debug_config_dict, "debug_log_config", build=build
+ )
+ 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 result
+
+
+#############################################
+# Common APIs, will be used by all protocols
+#############################################
+
+
+def create_vrf_cfg(tgen, topo, input_dict=None, build=False):
+ """
+ Create vrf configuration for created topology. VRF
+ configuration is provided in input json file.
+
+ VRF config is done in Linux Kernel:
+ * Create VRF
+ * Attach interface to VRF
+ * Bring up VRF
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring
+ from testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict={
+ "r3": {
+ "links": {
+ "r2-link1": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_A"},
+ "r2-link2": {"ipv4": "auto", "ipv6": "auto", "vrf": "RED_B"},
+ "r2-link3": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_A"},
+ "r2-link4": {"ipv4": "auto", "ipv6": "auto", "vrf": "BLUE_B"},
+ },
+ "vrfs":[
+ {
+ "name": "RED_A",
+ "id": "1"
+ },
+ {
+ "name": "RED_B",
+ "id": "2"
+ },
+ {
+ "name": "BLUE_A",
+ "id": "3",
+ "delete": True
+ },
+ {
+ "name": "BLUE_B",
+ "id": "4"
+ }
+ ]
+ }
+ }
+ result = create_vrf_cfg(tgen, topo, input_dict)
+
+ Returns
+ -------
+ True or False
+ """
+ result = True
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ input_dict = deepcopy(input_dict)
+
+ try:
+ config_data_dict = {}
+
+ for c_router, c_data in input_dict.items():
+ rnode = tgen.gears[c_router]
+ config_data = []
+ if "vrfs" in c_data:
+ for vrf in c_data["vrfs"]:
+ name = vrf.setdefault("name", None)
+ table_id = vrf.setdefault("id", None)
+ del_action = vrf.setdefault("delete", False)
+
+ if del_action:
+ # Kernel cmd- Add VRF and table
+ cmd = "ip link del {} type vrf table {}".format(
+ vrf["name"], vrf["id"]
+ )
+
+ logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd)
+ rnode.run(cmd)
+
+ # Kernel cmd - Bring down VRF
+ cmd = "ip link set dev {} down".format(name)
+ logger.info("[DUT: %s]: Running kernel cmd [%s]", c_router, cmd)
+ rnode.run(cmd)
+
+ else:
+ if name and table_id:
+ # Kernel cmd- Add VRF and table
+ cmd = "ip link add {} type vrf table {}".format(
+ name, table_id
+ )
+ logger.info(
+ "[DUT: %s]: Running kernel cmd " "[%s]", c_router, cmd
+ )
+ rnode.run(cmd)
+
+ # Kernel cmd - Bring up VRF
+ cmd = "ip link set dev {} up".format(name)
+ logger.info(
+ "[DUT: %s]: Running kernel " "cmd [%s]", c_router, cmd
+ )
+ rnode.run(cmd)
+
+ for vrf in c_data["vrfs"]:
+ vni = vrf.setdefault("vni", None)
+ del_vni = vrf.setdefault("no_vni", None)
+
+ if "links" in c_data:
+ for destRouterLink, data in sorted(c_data["links"].items()):
+ # Loopback interfaces
+ if "type" in data and data["type"] == "loopback":
+ interface_name = destRouterLink
+ else:
+ interface_name = data["interface"]
+
+ if "vrf" in data:
+ vrf_list = data["vrf"]
+
+ if type(vrf_list) is not list:
+ vrf_list = [vrf_list]
+
+ for _vrf in vrf_list:
+ cmd = "ip link set {} master {}".format(
+ interface_name, _vrf
+ )
+
+ logger.info(
+ "[DUT: %s]: Running" " kernel cmd [%s]",
+ c_router,
+ cmd,
+ )
+ 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)
+
+ if config_data:
+ config_data_dict[c_router] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "vrf", build=build
+ )
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ return result
+
+
+def create_interface_in_kernel(
+ tgen, dut, name, ip_addr, vrf=None, netmask=None, create=True
+):
+ """
+ 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
+ * `name` : interface name
+ * `ip_addr` : ip address for interface
+ * `vrf` : VRF name, to which interface will be associated
+ * `netmask` : netmask value, default is None
+ * `create`: Create interface in kernel, if created then no need
+ to create
+ """
+
+ rnode = tgen.gears[dut]
+
+ if create:
+ cmd = "ip link show {0} >/dev/null || ip link add {0} type dummy".format(name)
+ rnode.run(cmd)
+
+ if not netmask:
+ ifaddr = ipaddress.ip_interface(frr_unicode(ip_addr))
+ else:
+ ifaddr = ipaddress.ip_interface(
+ "{}/{}".format(frr_unicode(ip_addr), frr_unicode(netmask))
+ )
+ cmd = "ip -{0} a flush {1} scope global && ip a add {2} dev {1} && ip l set {1} up".format(
+ ifaddr.version, name, ifaddr
+ )
+ logger.info("[DUT: %s]: Running command: %s", dut, cmd)
+ rnode.run(cmd)
+
+ if vrf:
+ cmd = "ip link set {} master {}".format(name, vrf)
+ 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.gears[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
+ Parameters
+ ----------
+ * `ip_address`: IPv4/IPv6 address
+ Returns
+ -------
+ Type of address as string
+ """
+
+ if "/" in ip_address:
+ ip_address = ip_address.split("/")[0]
+
+ v4 = True
+ v6 = True
+ try:
+ socket.inet_aton(ip_address)
+ except socket.error as error:
+ logger.debug("Not a valid IPv4 address")
+ v4 = False
+ else:
+ return "ipv4"
+
+ try:
+ socket.inet_pton(socket.AF_INET6, ip_address)
+ except socket.error as error:
+ logger.debug("Not a valid IPv6 address")
+ v6 = False
+ else:
+ return "ipv6"
+
+ if not v4 and not v6:
+ raise Exception(
+ "InvalidIpAddr", "%s is neither valid IPv4 or IPv6" " address" % ip_address
+ )
+
+
+def check_address_types(addr_type=None):
+ """
+ Checks environment variable set and compares with the current address type
+ """
+
+ addr_types_env = os.environ.get("ADDRESS_TYPES")
+ if not addr_types_env:
+ addr_types_env = "dual"
+
+ if addr_types_env == "dual":
+ addr_types = ["ipv4", "ipv6"]
+ elif addr_types_env == "ipv4":
+ addr_types = ["ipv4"]
+ elif addr_types_env == "ipv6":
+ addr_types = ["ipv6"]
+
+ if addr_type is None:
+ return addr_types
+
+ if addr_type not in addr_types:
+ logger.debug(
+ "{} not in supported/configured address types {}".format(
+ addr_type, addr_types
+ )
+ )
+ return False
+
+ return True
+
+
+def generate_ips(network, no_of_ips):
+ """
+ Returns list of IPs.
+ based on start_ip and no_of_ips
+
+ * `network` : from here the ip will start generating,
+ start_ip will be
+ * `no_of_ips` : these many IPs will be generated
+ """
+ ipaddress_list = []
+ if type(network) is not list:
+ network = [network]
+
+ for start_ipaddr in network:
+ if "/" in start_ipaddr:
+ start_ip = start_ipaddr.split("/")[0]
+ mask = int(start_ipaddr.split("/")[1])
+ else:
+ logger.debug("start_ipaddr {} must have a / in it".format(start_ipaddr))
+ assert 0
+
+ addr_type = validate_ip_address(start_ip)
+ if addr_type == "ipv4":
+ if start_ip == "0.0.0.0" and mask == 0 and no_of_ips == 1:
+ ipaddress_list.append("{}/{}".format(start_ip, mask))
+ return ipaddress_list
+ start_ip = ipaddress.IPv4Address(frr_unicode(start_ip))
+ step = 2 ** (32 - mask)
+ elif addr_type == "ipv6":
+ if start_ip == "0::0" and mask == 0 and no_of_ips == 1:
+ ipaddress_list.append("{}/{}".format(start_ip, mask))
+ return ipaddress_list
+ start_ip = ipaddress.IPv6Address(frr_unicode(start_ip))
+ step = 2 ** (128 - mask)
+ else:
+ return []
+
+ next_ip = start_ip
+ count = 0
+ while count < no_of_ips:
+ ipaddress_list.append("{}/{}".format(next_ip, mask))
+ if addr_type == "ipv6":
+ next_ip = ipaddress.IPv6Address(int(next_ip) + step)
+ else:
+ next_ip += step
+ count += 1
+
+ return ipaddress_list
+
+
+def find_interface_with_greater_ip(topo, router, loopback=True, interface=True):
+ """
+ Returns highest interface ip for ipv4/ipv6. If loopback is there then
+ it will return highest IP from loopback IPs otherwise from physical
+ interface IPs.
+ * `topo` : json file data
+ * `router` : router for which highest interface should be calculated
+ """
+
+ link_data = topo["routers"][router]["links"]
+ lo_list = []
+ interfaces_list = []
+ lo_exists = False
+ for destRouterLink, data in sorted(link_data.items()):
+ if loopback:
+ if "type" in data and data["type"] == "loopback":
+ lo_exists = True
+ ip_address = topo["routers"][router]["links"][destRouterLink][
+ "ipv4"
+ ].split("/")[0]
+ lo_list.append(ip_address)
+ if interface:
+ ip_address = topo["routers"][router]["links"][destRouterLink]["ipv4"].split(
+ "/"
+ )[0]
+ interfaces_list.append(ip_address)
+
+ if lo_exists:
+ return sorted(lo_list)[-1]
+
+ return sorted(interfaces_list)[-1]
+
+
+def write_test_header(tc_name):
+ """Display message at beginning of test case"""
+ count = 20
+ logger.info("*" * (len(tc_name) + count))
+ step("START -> Testcase : %s" % tc_name, reset=True)
+ logger.info("*" * (len(tc_name) + count))
+
+
+def write_test_footer(tc_name):
+ """Display message at end of test case"""
+ count = 21
+ logger.info("=" * (len(tc_name) + count))
+ logger.info("Testcase : %s -> PASSED", tc_name)
+ logger.info("=" * (len(tc_name) + count))
+
+
+def interface_status(tgen, topo, input_dict):
+ """
+ Delete ip route maps from device
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : for which router, route map has to be deleted
+ Usage
+ -----
+ input_dict = {
+ "r3": {
+ "interface_list": ['eth1-r1-r2', 'eth2-r1-r3'],
+ "status": "down"
+ }
+ }
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ rlist = []
+
+ for router in input_dict.keys():
+
+ interface_list = input_dict[router]["interface_list"]
+ status = input_dict[router].setdefault("status", "up")
+ for intf in interface_list:
+ rnode = tgen.gears[router]
+ interface_set_status(rnode, intf, status)
+
+ rlist.append(router)
+
+ # Load config to routers
+ load_config_to_routers(tgen, rlist)
+
+ except Exception as e:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def retry(retry_timeout, initial_wait=0, expected=True, diag_pct=0.75):
+ """
+ Fixture: Retries function while it's return value is an errormsg (str), False, or it raises an exception.
+
+ * `retry_timeout`: Retry for at least this many seconds; after waiting initial_wait seconds
+ * `initial_wait`: Sleeps for this many seconds before first executing function
+ * `expected`: if False then the return logic is inverted, except for exceptions,
+ (i.e., a False or errmsg (str) function return ends the retry loop,
+ and returns that False or str value)
+ * `diag_pct`: Percentage of `retry_timeout` to keep testing after negative result would have
+ been returned in order to see if a positive result comes after. This is an
+ important diagnostic tool, and normally should not be disabled. Calls to wrapped
+ functions though, can override the `diag_pct` value to make it larger in case more
+ diagnostic retrying is appropriate.
+ """
+
+ def _retry(func):
+ @wraps(func)
+ def func_retry(*args, **kwargs):
+ # We will continue to retry diag_pct of the timeout value to see if test would have passed with a
+ # longer retry timeout value.
+ saved_failure = None
+
+ retry_sleep = 2
+
+ # Allow the wrapped function's args to override the fixtures
+ _retry_timeout = kwargs.pop("retry_timeout", retry_timeout)
+ _expected = kwargs.pop("expected", expected)
+ _initial_wait = kwargs.pop("initial_wait", initial_wait)
+ _diag_pct = kwargs.pop("diag_pct", diag_pct)
+
+ start_time = datetime.now()
+ retry_until = datetime.now() + timedelta(
+ seconds=_retry_timeout + _initial_wait
+ )
+
+ if initial_wait > 0:
+ logger.info("Waiting for [%s]s as initial delay", initial_wait)
+ sleep(initial_wait)
+
+ invert_logic = not _expected
+ while True:
+ seconds_left = (retry_until - datetime.now()).total_seconds()
+ try:
+ ret = func(*args, **kwargs)
+ logger.debug("Function returned %s", ret)
+
+ negative_result = ret is False or is_string(ret)
+ if negative_result == invert_logic:
+ # Simple case, successful result in time
+ if not saved_failure:
+ return ret
+
+ # Positive result, but happened after timeout failure, very important to
+ # note for fixing tests.
+ logger.warning(
+ "RETRY DIAGNOSTIC: SUCCEED after FAILED with requested timeout of %.1fs; however, succeeded in %.1fs, investigate timeout timing",
+ _retry_timeout,
+ (datetime.now() - start_time).total_seconds(),
+ )
+ if isinstance(saved_failure, Exception):
+ raise saved_failure # pylint: disable=E0702
+ return saved_failure
+
+ except Exception as error:
+ logger.info("Function raised exception: %s", str(error))
+ ret = error
+
+ if seconds_left < 0 and saved_failure:
+ logger.info(
+ "RETRY DIAGNOSTIC: Retry timeout reached, still failing"
+ )
+ if isinstance(saved_failure, Exception):
+ raise saved_failure # pylint: disable=E0702
+ return saved_failure
+
+ if seconds_left < 0:
+ logger.info("Retry timeout of %ds reached", _retry_timeout)
+
+ saved_failure = ret
+ retry_extra_delta = timedelta(
+ seconds=seconds_left + _retry_timeout * _diag_pct
+ )
+ retry_until = datetime.now() + retry_extra_delta
+ seconds_left = retry_extra_delta.total_seconds()
+
+ # Generate bundle after setting remaining diagnostic retry time
+ generate_support_bundle()
+
+ # If user has disabled diagnostic retries return now
+ if not _diag_pct:
+ if isinstance(saved_failure, Exception):
+ raise saved_failure
+ return saved_failure
+
+ if saved_failure:
+ logger.info(
+ "RETRY DIAG: [failure] Sleeping %ds until next retry with %.1f retry time left - too see if timeout was too short",
+ retry_sleep,
+ seconds_left,
+ )
+ else:
+ logger.info(
+ "Sleeping %ds until next retry with %.1f retry time left",
+ retry_sleep,
+ seconds_left,
+ )
+ sleep(retry_sleep)
+
+ func_retry._original = func
+ return func_retry
+
+ return _retry
+
+
+class Stepper:
+ """
+ Prints step number for the test case step being executed
+ """
+
+ count = 1
+
+ def __call__(self, msg, reset):
+ if reset:
+ Stepper.count = 1
+ logger.info(msg)
+ else:
+ logger.info("STEP %s: '%s'", Stepper.count, msg)
+ Stepper.count += 1
+
+
+def step(msg, reset=False):
+ """
+ Call Stepper to print test steps. Need to reset at the beginning of test.
+ * ` msg` : Step message body.
+ * `reset` : Reset step count to 1 when set to True.
+ """
+ _step = Stepper()
+ _step(msg, reset)
+
+
+def do_countdown(secs):
+ """
+ Countdown timer display
+ """
+ for i in range(secs, 0, -1):
+ sys.stdout.write("{} ".format(str(i)))
+ sys.stdout.flush()
+ sleep(1)
+ return
+
+
+#############################################
+# These APIs, will used by testcase
+#############################################
+def create_interfaces_cfg(tgen, topo, build=False):
+ """
+ Create interface configuration for created topology. Basic Interface
+ configuration is provided in input json file.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `build` : Only for initial setup phase this is set as True.
+
+ Returns
+ -------
+ True or False
+ """
+
+ def _create_interfaces_ospf_cfg(ospf, c_data, data, ospf_keywords):
+ interface_data = []
+ ip_ospf = "ipv6 ospf6" if ospf == "ospf6" else "ip ospf"
+ for keyword in ospf_keywords:
+ if keyword in data[ospf]:
+ intf_ospf_value = c_data["links"][destRouterLink][ospf][keyword]
+ if "delete" in data and data["delete"]:
+ interface_data.append(
+ "no {} {}".format(ip_ospf, keyword.replace("_", "-"))
+ )
+ else:
+ interface_data.append(
+ "{} {} {}".format(
+ ip_ospf, keyword.replace("_", "-"), intf_ospf_value
+ )
+ )
+ return interface_data
+
+ result = False
+ topo = deepcopy(topo)
+
+ try:
+ interface_data_dict = {}
+
+ for c_router, c_data in topo.items():
+ interface_data = []
+ for destRouterLink, data in sorted(c_data["links"].items()):
+ # Loopback interfaces
+ if "type" in data and data["type"] == "loopback":
+ interface_name = destRouterLink
+ else:
+ interface_name = data["interface"]
+
+ interface_data.append("interface {}".format(str(interface_name)))
+
+ if "ipv4" in data:
+ intf_addr = c_data["links"][destRouterLink]["ipv4"]
+
+ if "delete" in data and data["delete"]:
+ interface_data.append("no ip address {}".format(intf_addr))
+ else:
+ interface_data.append("ip address {}".format(intf_addr))
+ if "ipv6" in data:
+ intf_addr = c_data["links"][destRouterLink]["ipv6"]
+
+ if "delete" in data and data["delete"]:
+ interface_data.append("no ipv6 address {}".format(intf_addr))
+ else:
+ interface_data.append("ipv6 address {}".format(intf_addr))
+
+ # Wait for vrf interfaces to get link local address once they are up
+ if (
+ not destRouterLink == "lo"
+ and "vrf" in topo[c_router]["links"][destRouterLink]
+ ):
+ vrf = topo[c_router]["links"][destRouterLink]["vrf"]
+ intf = topo[c_router]["links"][destRouterLink]["interface"]
+ ll = get_frr_ipv6_linklocal(tgen, c_router, intf=intf, vrf=vrf)
+
+ if "ipv6-link-local" in data:
+ intf_addr = c_data["links"][destRouterLink]["ipv6-link-local"]
+
+ if "delete" in data and data["delete"]:
+ interface_data.append("no ipv6 address {}".format(intf_addr))
+ else:
+ interface_data.append("ipv6 address {}\n".format(intf_addr))
+
+ ospf_keywords = [
+ "hello_interval",
+ "dead_interval",
+ "network",
+ "priority",
+ "cost",
+ "mtu_ignore",
+ ]
+ if "ospf" in data:
+ interface_data += _create_interfaces_ospf_cfg(
+ "ospf", c_data, data, ospf_keywords + ["area"]
+ )
+ if "ospf6" in data:
+ interface_data += _create_interfaces_ospf_cfg(
+ "ospf6", c_data, data, ospf_keywords + ["area"]
+ )
+ if interface_data:
+ interface_data_dict[c_router] = interface_data
+
+ result = create_common_configurations(
+ tgen, interface_data_dict, "interface_config", build=build
+ )
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ return result
+
+
+def create_static_routes(tgen, input_dict, build=False):
+ """
+ Create static routes for given router as defined in input_dict
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict should be in the format below:
+ # static_routes: list of all routes
+ # network: network address
+ # no_of_ip: number of next-hop address that will be configured
+ # admin_distance: admin distance for route/routes.
+ # next_hop: starting next-hop address
+ # tag: tag id for static routes
+ # vrf: VRF name in which static routes needs to be created
+ # delete: True if config to be removed. Default False.
+
+ Example:
+ "routers": {
+ "r1": {
+ "static_routes": [
+ {
+ "network": "100.0.20.1/32",
+ "no_of_ip": 9,
+ "admin_distance": 100,
+ "next_hop": "10.0.0.1",
+ "tag": 4001,
+ "vrf": "RED_A"
+ "delete": true
+ }
+ ]
+ }
+ }
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ result = False
+ logger.debug("Entering lib API: create_static_routes()")
+ input_dict = deepcopy(input_dict)
+
+ try:
+ static_routes_list_dict = {}
+
+ for router in input_dict.keys():
+ if "static_routes" not in input_dict[router]:
+ errormsg = "static_routes not present in input_dict"
+ logger.info(errormsg)
+ continue
+
+ static_routes_list = []
+
+ static_routes = input_dict[router]["static_routes"]
+ for static_route in static_routes:
+ del_action = static_route.setdefault("delete", False)
+ no_of_ip = static_route.setdefault("no_of_ip", 1)
+ network = static_route.setdefault("network", [])
+ if type(network) is not list:
+ network = [network]
+
+ admin_distance = static_route.setdefault("admin_distance", None)
+ tag = static_route.setdefault("tag", None)
+ vrf = static_route.setdefault("vrf", None)
+ interface = static_route.setdefault("interface", None)
+ next_hop = static_route.setdefault("next_hop", None)
+ nexthop_vrf = static_route.setdefault("nexthop_vrf", None)
+
+ ip_list = generate_ips(network, no_of_ip)
+ for ip in ip_list:
+ addr_type = validate_ip_address(ip)
+
+ if addr_type == "ipv4":
+ cmd = "ip route {}".format(ip)
+ else:
+ cmd = "ipv6 route {}".format(ip)
+
+ if interface:
+ cmd = "{} {}".format(cmd, interface)
+
+ if next_hop:
+ cmd = "{} {}".format(cmd, next_hop)
+
+ if nexthop_vrf:
+ cmd = "{} nexthop-vrf {}".format(cmd, nexthop_vrf)
+
+ if vrf:
+ cmd = "{} vrf {}".format(cmd, vrf)
+
+ if tag:
+ cmd = "{} tag {}".format(cmd, str(tag))
+
+ if admin_distance:
+ cmd = "{} {}".format(cmd, admin_distance)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ static_routes_list.append(cmd)
+
+ if static_routes_list:
+ static_routes_list_dict[router] = static_routes_list
+
+ result = create_common_configurations(
+ tgen, static_routes_list_dict, "static_route", build=build
+ )
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: create_static_routes()")
+ return result
+
+
+def create_prefix_lists(tgen, input_dict, build=False):
+ """
+ Create ip prefix lists as per the config provided in input
+ JSON or input_dict
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ Usage
+ -----
+ # pf_lists_1: name of prefix-list, user defined
+ # seqid: prefix-list seqid, auto-generated if not given by user
+ # network: criteria for applying prefix-list
+ # action: permit/deny
+ # le: less than or equal number of bits
+ # ge: greater than or equal number of bits
+ Example
+ -------
+ input_dict = {
+ "r1": {
+ "prefix_lists":{
+ "ipv4": {
+ "pf_list_1": [
+ {
+ "seqid": 10,
+ "network": "any",
+ "action": "permit",
+ "le": "32",
+ "ge": "30",
+ "delete": True
+ }
+ ]
+ }
+ }
+ }
+ }
+ Returns
+ -------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ try:
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ if "prefix_lists" not in input_dict[router]:
+ errormsg = "prefix_lists not present in input_dict"
+ logger.debug(errormsg)
+ continue
+
+ config_data = []
+ prefix_lists = input_dict[router]["prefix_lists"]
+ for addr_type, prefix_data in prefix_lists.items():
+ if not check_address_types(addr_type):
+ continue
+
+ for prefix_name, prefix_list in prefix_data.items():
+ for prefix_dict in prefix_list:
+ if "action" not in prefix_dict or "network" not in prefix_dict:
+ errormsg = "'action' or network' missing in" " input_dict"
+ return errormsg
+
+ network_addr = prefix_dict["network"]
+ action = prefix_dict["action"]
+ le = prefix_dict.setdefault("le", None)
+ ge = prefix_dict.setdefault("ge", None)
+ seqid = prefix_dict.setdefault("seqid", None)
+ del_action = prefix_dict.setdefault("delete", False)
+ if seqid is None:
+ seqid = get_seq_id("prefix_lists", router, prefix_name)
+ else:
+ set_seq_id("prefix_lists", router, seqid, prefix_name)
+
+ if addr_type == "ipv4":
+ protocol = "ip"
+ else:
+ protocol = "ipv6"
+
+ cmd = "{} prefix-list {} seq {} {} {}".format(
+ protocol, prefix_name, seqid, action, network_addr
+ )
+ if le:
+ cmd = "{} le {}".format(cmd, le)
+ if ge:
+ cmd = "{} ge {}".format(cmd, ge)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+ if config_data:
+ config_data_dict[router] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "prefix_list", build=build
+ )
+
+ 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 result
+
+
+def create_route_maps(tgen, input_dict, build=False):
+ """
+ Create route-map on the devices as per the arguments passed
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ Usage
+ -----
+ # route_maps: key, value pair for route-map name and its attribute
+ # rmap_match_prefix_list_1: user given name for route-map
+ # action: PERMIT/DENY
+ # match: key,value pair for match criteria. prefix_list, community-list,
+ large-community-list or tag. Only one option at a time.
+ # prefix_list: name of prefix list
+ # large-community-list: name of large community list
+ # community-ist: name of community list
+ # tag: tag id for static routes
+ # set: key, value pair for modifying route attributes
+ # localpref: preference value for the network
+ # med: metric value advertised for AS
+ # aspath: set AS path value
+ # weight: weight for the route
+ # community: standard community value to be attached
+ # large_community: large community value to be attached
+ # community_additive: if set to "additive", adds community/large-community
+ value to the existing values of the network prefix
+ Example:
+ --------
+ input_dict = {
+ "r1": {
+ "route_maps": {
+ "rmap_match_prefix_list_1": [
+ {
+ "action": "PERMIT",
+ "match": {
+ "ipv4": {
+ "prefix_list": "pf_list_1"
+ }
+ "ipv6": {
+ "prefix_list": "pf_list_1"
+ }
+ "large-community-list": {
+ "id": "community_1",
+ "exact_match": True
+ }
+ "community_list": {
+ "id": "community_2",
+ "exact_match": True
+ }
+ "tag": "tag_id"
+ },
+ "set": {
+ "locPrf": 150,
+ "metric": 30,
+ "path": {
+ "num": 20000,
+ "action": "prepend",
+ },
+ "weight": 500,
+ "community": {
+ "num": "1:2 2:3",
+ "action": additive
+ }
+ "large_community": {
+ "num": "1:2:3 4:5;6",
+ "action": additive
+ },
+ }
+ }
+ ]
+ }
+ }
+ }
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ input_dict = deepcopy(input_dict)
+ try:
+ rmap_data_dict = {}
+
+ for router in input_dict.keys():
+ if "route_maps" not in input_dict[router]:
+ logger.debug("route_maps not present in input_dict")
+ continue
+ rmap_data = []
+ for rmap_name, rmap_value in input_dict[router]["route_maps"].items():
+
+ for rmap_dict in rmap_value:
+ del_action = rmap_dict.setdefault("delete", False)
+
+ if del_action:
+ rmap_data.append("no route-map {}".format(rmap_name))
+ continue
+
+ if "action" not in rmap_dict:
+ errormsg = "action not present in input_dict"
+ logger.error(errormsg)
+ return False
+
+ rmap_action = rmap_dict.setdefault("action", "deny")
+
+ seq_id = rmap_dict.setdefault("seq_id", None)
+ if seq_id is None:
+ seq_id = get_seq_id("route_maps", router, rmap_name)
+ else:
+ set_seq_id("route_maps", router, seq_id, rmap_name)
+
+ rmap_data.append(
+ "route-map {} {} {}".format(rmap_name, rmap_action, seq_id)
+ )
+
+ if "continue" in rmap_dict:
+ continue_to = rmap_dict["continue"]
+ if continue_to:
+ rmap_data.append("on-match goto {}".format(continue_to))
+ else:
+ logger.error(
+ "In continue, 'route-map entry "
+ "sequence number' is not provided"
+ )
+ return False
+
+ if "goto" in rmap_dict:
+ go_to = rmap_dict["goto"]
+ if go_to:
+ rmap_data.append("on-match goto {}".format(go_to))
+ else:
+ logger.error(
+ "In goto, 'Goto Clause number' is not" " provided"
+ )
+ return False
+
+ if "call" in rmap_dict:
+ call_rmap = rmap_dict["call"]
+ if call_rmap:
+ rmap_data.append("call {}".format(call_rmap))
+ else:
+ logger.error(
+ "In call, 'destination Route-Map' is" " not provided"
+ )
+ return False
+
+ # Verifying if SET criteria is defined
+ if "set" in rmap_dict:
+ set_data = rmap_dict["set"]
+ ipv4_data = set_data.setdefault("ipv4", {})
+ ipv6_data = set_data.setdefault("ipv6", {})
+ local_preference = set_data.setdefault("locPrf", None)
+ metric = set_data.setdefault("metric", None)
+ metric_type = set_data.setdefault("metric-type", None)
+ as_path = set_data.setdefault("path", {})
+ weight = set_data.setdefault("weight", None)
+ community = set_data.setdefault("community", {})
+ large_community = set_data.setdefault("large_community", {})
+ large_comm_list = set_data.setdefault("large_comm_list", {})
+ 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", {})
+ metrictype = set_data.setdefault("metric-type", {})
+
+ # Local Preference
+ if local_preference:
+ rmap_data.append(
+ "set local-preference {}".format(local_preference)
+ )
+
+ # Metric-Type
+ if metrictype:
+ rmap_data.append("set metric-type {}\n".format(metrictype))
+
+ # Metric
+ if metric:
+ del_comm = set_data.setdefault("delete", None)
+ if del_comm:
+ rmap_data.append("no set metric {}".format(metric))
+ else:
+ rmap_data.append("set metric {}".format(metric))
+
+ # Origin
+ if origin:
+ rmap_data.append("set origin {} \n".format(origin))
+
+ # AS Path Prepend
+ if as_path:
+ as_num = as_path.setdefault("as_num", None)
+ as_action = as_path.setdefault("as_action", None)
+ if as_action and as_num:
+ rmap_data.append(
+ "set as-path {} {}".format(as_action, as_num)
+ )
+
+ # Community
+ if community:
+ num = community.setdefault("num", None)
+ comm_action = community.setdefault("action", None)
+ if num:
+ cmd = "set community {}".format(num)
+ if comm_action:
+ cmd = "{} {}".format(cmd, comm_action)
+ rmap_data.append(cmd)
+ else:
+ logger.error("In community, AS Num not" " provided")
+ return False
+
+ if large_community:
+ num = large_community.setdefault("num", None)
+ comm_action = large_community.setdefault("action", None)
+ if num:
+ cmd = "set large-community {}".format(num)
+ if comm_action:
+ cmd = "{} {}".format(cmd, comm_action)
+
+ rmap_data.append(cmd)
+ else:
+ logger.error(
+ "In large_community, AS Num not" " provided"
+ )
+ return False
+ if large_comm_list:
+ id = large_comm_list.setdefault("id", None)
+ del_comm = large_comm_list.setdefault("delete", None)
+ if id:
+ cmd = "set large-comm-list {}".format(id)
+ if del_comm:
+ cmd = "{} delete".format(cmd)
+
+ rmap_data.append(cmd)
+ else:
+ 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))
+ if ipv6_data:
+ nexthop = ipv6_data.setdefault("nexthop", None)
+ if nexthop:
+ rmap_data.append("set ipv6 next-hop {}".format(nexthop))
+
+ # Adding MATCH and SET sequence to RMAP if defined
+ if "match" in rmap_dict:
+ match_data = rmap_dict["match"]
+ ipv4_data = match_data.setdefault("ipv4", {})
+ ipv6_data = match_data.setdefault("ipv6", {})
+ community = match_data.setdefault("community_list", {})
+ large_community = match_data.setdefault("large_community", {})
+ large_community_list = match_data.setdefault(
+ "large_community_list", {}
+ )
+
+ metric = match_data.setdefault("metric", None)
+ source_vrf = match_data.setdefault("source-vrf", None)
+
+ if ipv4_data:
+ # fetch prefix list data from rmap
+ prefix_name = ipv4_data.setdefault("prefix_lists", None)
+ if prefix_name:
+ rmap_data.append(
+ "match ip address"
+ " prefix-list {}".format(prefix_name)
+ )
+
+ # fetch tag data from rmap
+ tag = ipv4_data.setdefault("tag", None)
+ if tag:
+ rmap_data.append("match tag {}".format(tag))
+
+ # fetch large community data from rmap
+ large_community_list = ipv4_data.setdefault(
+ "large_community_list", {}
+ )
+ large_community = match_data.setdefault(
+ "large_community", {}
+ )
+
+ if ipv6_data:
+ prefix_name = ipv6_data.setdefault("prefix_lists", None)
+ if prefix_name:
+ rmap_data.append(
+ "match ipv6 address"
+ " prefix-list {}".format(prefix_name)
+ )
+
+ # fetch tag data from rmap
+ tag = ipv6_data.setdefault("tag", None)
+ if tag:
+ rmap_data.append("match tag {}".format(tag))
+
+ # fetch large community data from rmap
+ large_community_list = ipv6_data.setdefault(
+ "large_community_list", {}
+ )
+ large_community = match_data.setdefault(
+ "large_community", {}
+ )
+
+ if community:
+ if "id" not in community:
+ logger.error(
+ "'id' is mandatory for "
+ "community-list in match"
+ " criteria"
+ )
+ return False
+ cmd = "match community {}".format(community["id"])
+ exact_match = community.setdefault("exact_match", False)
+ if exact_match:
+ cmd = "{} exact-match".format(cmd)
+
+ rmap_data.append(cmd)
+ if large_community:
+ if "id" not in large_community:
+ logger.error(
+ "'id' is mandatory for "
+ "large-community-list in match "
+ "criteria"
+ )
+ return False
+ cmd = "match large-community {}".format(
+ large_community["id"]
+ )
+ exact_match = large_community.setdefault(
+ "exact_match", False
+ )
+ if exact_match:
+ cmd = "{} exact-match".format(cmd)
+ rmap_data.append(cmd)
+ if large_community_list:
+ if "id" not in large_community_list:
+ logger.error(
+ "'id' is mandatory for "
+ "large-community-list in match "
+ "criteria"
+ )
+ return False
+ cmd = "match large-community {}".format(
+ large_community_list["id"]
+ )
+ exact_match = large_community_list.setdefault(
+ "exact_match", False
+ )
+ if exact_match:
+ cmd = "{} exact-match".format(cmd)
+ rmap_data.append(cmd)
+
+ if source_vrf:
+ cmd = "match source-vrf {}".format(source_vrf)
+ rmap_data.append(cmd)
+
+ if metric:
+ cmd = "match metric {}".format(metric)
+ rmap_data.append(cmd)
+
+ if rmap_data:
+ rmap_data_dict[router] = rmap_data
+
+ result = create_common_configurations(
+ tgen, rmap_data_dict, "route_maps", build=build
+ )
+
+ 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 result
+
+
+def delete_route_maps(tgen, input_dict):
+ """
+ Delete ip route maps from device
+ * `tgen` : Topogen object
+ * `input_dict` : for which router,
+ route map has to be deleted
+ Usage
+ -----
+ # Delete route-map rmap_1 and rmap_2 from router r1
+ input_dict = {
+ "r1": {
+ "route_maps": ["rmap_1", "rmap__2"]
+ }
+ }
+ result = delete_route_maps("ipv4", 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():
+ route_maps = input_dict[router]["route_maps"][:]
+ rmap_data = input_dict[router]
+ rmap_data["route_maps"] = {}
+ for route_map_name in route_maps:
+ rmap_data["route_maps"].update({route_map_name: [{"delete": True}]})
+
+ return create_route_maps(tgen, input_dict)
+
+
+def create_bgp_community_lists(tgen, input_dict, build=False):
+ """
+ Create bgp community-list or large-community-list on the devices as per
+ the arguments passed. Takes list of communities in input.
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ Usage
+ -----
+ input_dict_1 = {
+ "r3": {
+ "bgp_community_lists": [
+ {
+ "community_type": "standard",
+ "action": "permit",
+ "name": "rmap_lcomm_{}".format(addr_type),
+ "value": "1:1:1 1:2:3 2:1:1 2:2:2",
+ "large": True
+ }
+ ]
+ }
+ }
+ }
+ result = create_bgp_community_lists(tgen, input_dict_1)
+ """
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ input_dict = deepcopy(input_dict)
+ try:
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ if "bgp_community_lists" not in input_dict[router]:
+ errormsg = "bgp_community_lists not present in input_dict"
+ logger.debug(errormsg)
+ continue
+
+ config_data = []
+
+ community_list = input_dict[router]["bgp_community_lists"]
+ for community_dict in community_list:
+ del_action = community_dict.setdefault("delete", False)
+ community_type = community_dict.setdefault("community_type", None)
+ action = community_dict.setdefault("action", None)
+ value = community_dict.setdefault("value", "")
+ large = community_dict.setdefault("large", None)
+ name = community_dict.setdefault("name", None)
+ if large:
+ cmd = "bgp large-community-list"
+ else:
+ cmd = "bgp community-list"
+
+ if not large and not (community_type and action and value):
+ errormsg = (
+ "community_type, action and value are "
+ "required in bgp_community_list"
+ )
+ logger.error(errormsg)
+ return False
+
+ cmd = "{} {} {} {} {}".format(cmd, community_type, name, action, value)
+
+ if del_action:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+
+ if config_data:
+ config_data_dict[router] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "bgp_community_list", build=build
+ )
+
+ 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 result
+
+
+def shutdown_bringup_interface(tgen, dut, intf_name, ifaceaction=False):
+ """
+ Shutdown or bringup router's interface "
+ * `tgen` : Topogen object
+ * `dut` : Device under test
+ * `intf_name` : Interface name to be shut/no shut
+ * `ifaceaction` : Action, to shut/no shut interface,
+ by default is False
+ Usage
+ -----
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ # Shut down interface
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ # Bring up interface
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ router_list = tgen.routers()
+ if ifaceaction:
+ logger.info("Bringing up interface {} : {}".format(dut, intf_name))
+ else:
+ logger.info("Shutting down interface {} : {}".format(dut, intf_name))
+
+ interface_set_status(router_list[dut], intf_name, ifaceaction)
+
+
+def addKernelRoute(
+ tgen, router, intf, group_addr_range, next_hop=None, src=None, del_action=None
+):
+ """
+ Add route to kernel
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `router`: router for which kernel routes needs to be added
+ * `intf`: interface name, for which kernel routes needs to be added
+ * `bindToAddress`: bind to <host>, an interface or multicast
+ address
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: addKernelRoute()")
+
+ rnode = tgen.gears[router]
+
+ if type(group_addr_range) is not list:
+ group_addr_range = [group_addr_range]
+
+ for grp_addr in group_addr_range:
+
+ addr_type = validate_ip_address(grp_addr)
+ if addr_type == "ipv4":
+ if next_hop is not None:
+ cmd = "ip route add {} via {}".format(grp_addr, next_hop)
+ else:
+ cmd = "ip route add {} dev {}".format(grp_addr, intf)
+ if del_action:
+ cmd = "ip route del {}".format(grp_addr)
+ verify_cmd = "ip route"
+ elif addr_type == "ipv6":
+ if intf and src:
+ cmd = "ip -6 route add {} dev {} src {}".format(grp_addr, intf, src)
+ else:
+ cmd = "ip -6 route add {} via {}".format(grp_addr, next_hop)
+ verify_cmd = "ip -6 route"
+ if del_action:
+ cmd = "ip -6 route del {}".format(grp_addr)
+
+ logger.info("[DUT: {}]: Running command: [{}]".format(router, cmd))
+ output = rnode.run(cmd)
+
+ def check_in_kernel(rnode, verify_cmd, grp_addr, router):
+ # Verifying if ip route added to kernel
+ errormsg = None
+ result = rnode.run(verify_cmd)
+ logger.debug("{}\n{}".format(verify_cmd, result))
+ if "/" in grp_addr:
+ ip, mask = grp_addr.split("/")
+ if mask == "32" or mask == "128":
+ grp_addr = ip
+ else:
+ mask = "32" if addr_type == "ipv4" else "128"
+
+ if not re_search(r"{}".format(grp_addr), result) and mask != "0":
+ errormsg = (
+ "[DUT: {}]: Kernal route is not added for group"
+ " address {} Config output: {}".format(
+ router, grp_addr, output
+ )
+ )
+
+ return errormsg
+
+ test_func = functools.partial(
+ check_in_kernel, rnode, verify_cmd, grp_addr, router
+ )
+ (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result, out
+
+ logger.debug("Exiting lib API: addKernelRoute()")
+ return True
+
+
+def configure_vxlan(tgen, input_dict):
+ """
+ Add and configure vxlan
+
+ * `tgen`: tgen object
+ * `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 = router_list[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 object
+ * `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 = router_list[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 object
+ * `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 = router_list[dut]
+
+ for intf, mac in input_dict[dut].items():
+ cmd = "ip link set {} address {}".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
+
+
+def socat_send_igmp_join_traffic(
+ tgen,
+ server,
+ protocol_option,
+ igmp_groups,
+ send_from_intf,
+ send_from_intf_ip=None,
+ port=12345,
+ reuseaddr=True,
+ join=False,
+ traffic=False,
+):
+ """
+ API to send IGMP join using SOCAT tool
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `server`: iperf server, from where IGMP join would be sent
+ * `protocol_option`: Protocol options, ex: UDP6-RECV
+ * `igmp_groups`: IGMP group for which join has to be sent
+ * `send_from_intf`: Interface from which join would be sent
+ * `send_from_intf_ip`: Interface IP, default is None
+ * `port`: Port to be used, default is 12345
+ * `reuseaddr`: True|False, bydefault True
+ * `join`: If join needs to be sent
+ * `traffic`: If traffic needs to be sent
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ rnode = tgen.routers()[server]
+ socat_cmd = "socat -u "
+
+ # UDP4/TCP4/UDP6/UDP6-RECV
+ if protocol_option:
+ socat_cmd += "{}".format(protocol_option)
+
+ if port:
+ socat_cmd += ":{},".format(port)
+
+ if reuseaddr:
+ socat_cmd += "{},".format("reuseaddr")
+
+ # Group address range to cover
+ if igmp_groups:
+ if not isinstance(igmp_groups, list):
+ igmp_groups = [igmp_groups]
+
+ for igmp_group in igmp_groups:
+ if join:
+ join_traffic_option = "ipv6-join-group"
+ elif traffic:
+ join_traffic_option = "ipv6-join-group-source"
+
+ if send_from_intf and not send_from_intf_ip:
+ socat_cmd += "{}='[{}]:{}'".format(
+ join_traffic_option, igmp_group, send_from_intf
+ )
+ else:
+ socat_cmd += "{}='[{}]:{}:[{}]'".format(
+ join_traffic_option, igmp_group, send_from_intf, send_from_intf_ip
+ )
+
+ socat_cmd += " STDOUT"
+
+ socat_cmd += " &>{}/socat.logs &".format(tgen.logdir)
+
+ # Run socat command to send IGMP join
+ logger.info("[DUT: {}]: Running command: [{}]".format(server, socat_cmd))
+ output = rnode.run("set +m; {} sleep 0.5".format(socat_cmd))
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+#############################################
+# Verification APIs
+#############################################
+@retry(retry_timeout=40)
+def verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict,
+ next_hop=None,
+ protocol=None,
+ tag=None,
+ metric=None,
+ fib=None,
+ count_only=False,
+ admin_distance=None,
+):
+ """
+ Data will be read from input_dict or input JSON file, API will generate
+ same prefixes, which were redistributed by either create_static_routes() or
+ advertise_networks_using_network_command() and do will verify next_hop and
+ each prefix/routes is present in "show ip/ipv6 route {bgp/stataic} json"
+ command o/p.
+
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test, for which user wants to test the data
+ * `input_dict` : input dict, has details of static routes
+ * `next_hop`[optional]: next_hop which needs to be verified,
+ default: static
+ * `protocol`[optional]: protocol, default = None
+ * `count_only`[optional]: count of nexthops only, not specific addresses,
+ default = False
+
+ Usage
+ -----
+ # RIB can be verified for static routes OR network advertised using
+ network command. Following are input_dicts to create static routes
+ and advertise networks using network command. Any one of the input_dict
+ can be passed to verify_rib() to verify routes in DUT"s RIB.
+
+ # Creating static routes for r1
+ input_dict = {
+ "r1": {
+ "static_routes": [{"network": "10.0.20.1/32", "no_of_ip": 9, \
+ "admin_distance": 100, "next_hop": "10.0.0.2", "tag": 4001}]
+ }}
+ # Advertising networks using network command in router r1
+ input_dict = {
+ "r1": {
+ "advertise_networks": [{"start_ip": "20.0.0.0/32",
+ "no_of_network": 10},
+ {"start_ip": "30.0.0.0/32"}]
+ }}
+ # Verifying ipv4 routes in router r1 learned via BGP
+ dut = "r2"
+ protocol = "bgp"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol = protocol)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s RIB:", router)
+
+ # Verifying RIB routes
+ if addr_type == "ipv4":
+ command = "show ip route"
+ else:
+ command = "show ipv6 route"
+
+ found_routes = []
+ missing_routes = []
+
+ if "static_routes" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ if "vrf" in static_route and static_route["vrf"] is not None:
+
+ logger.info(
+ "[DUT: {}]: Verifying routes for VRF:"
+ " {}".format(router, static_route["vrf"])
+ )
+
+ cmd = "{} vrf {}".format(command, static_route["vrf"])
+
+ else:
+ cmd = "{}".format(command)
+
+ if protocol:
+ cmd = "{} {}".format(cmd, protocol)
+
+ cmd = "{} json".format(cmd)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) is False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+
+ network = static_route["network"]
+ if "no_of_ip" in static_route:
+ no_of_ip = static_route["no_of_ip"]
+ else:
+ no_of_ip = 1
+
+ if "tag" in static_route:
+ _tag = static_route["tag"]
+ else:
+ _tag = None
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+ st_found = False
+ nh_found = False
+
+ for st_rt in ip_list:
+ st_rt = str(
+ ipaddress.ip_network(frr_unicode(st_rt), strict=False)
+ )
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if "queued" in rib_routes_json[st_rt][0]:
+ errormsg = "Route {} is queued\n".format(st_rt)
+ return errormsg
+
+ if fib and next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ for mnh in range(0, len(rib_routes_json[st_rt])):
+ if not "selected" in rib_routes_json[st_rt][mnh]:
+ continue
+
+ if (
+ "fib"
+ in rib_routes_json[st_rt][mnh]["nexthops"][0]
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in rib_routes_json[st_rt][
+ mnh
+ ]["nexthops"]
+ ]
+ )
+
+ if found_hops[0]:
+ missing_list_of_nexthops = set(
+ found_hops[0]
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops[0])
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Nexthop "
+ "%s is not active for route %s in "
+ "RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is not active"
+ " for route {} in RIB of router"
+ " {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+
+ elif next_hop and fib is None:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ found_hops = [
+ rib_r["ip"]
+ for rib_r in rib_routes_json[st_rt][0]["nexthops"]
+ if "ip" in rib_r
+ ]
+
+ # If somehow key "ip" is not found in nexthops JSON
+ # then found_hops would be 0, this particular
+ # situation will be handled here
+ if not len(found_hops):
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ next_hop,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+
+ # Check only the count of nexthops
+ if count_only:
+ if len(next_hop) == len(found_hops):
+ nh_found = True
+ else:
+ errormsg = (
+ "Nexthops are missing for "
+ "route {} in RIB of router {}: "
+ "expected {}, found {}\n".format(
+ st_rt,
+ dut,
+ len(next_hop),
+ len(found_hops),
+ )
+ )
+ return errormsg
+
+ # Check the actual nexthops
+ elif found_hops:
+ missing_list_of_nexthops = set(
+ found_hops
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops)
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+
+ if tag:
+ if "tag" not in rib_routes_json[st_rt][0]:
+ errormsg = (
+ "[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if _tag != rib_routes_json[st_rt][0]["tag"]:
+ errormsg = (
+ "[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ if admin_distance is not None:
+ if "distance" not in rib_routes_json[st_rt][0]:
+ errormsg = (
+ "[DUT: {}]: admin distance is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if (
+ admin_distance
+ != rib_routes_json[st_rt][0]["distance"]
+ ):
+ errormsg = (
+ "[DUT: {}]: admin distance value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ admin_distance,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ if metric is not None:
+ if "metric" not in rib_routes_json[st_rt][0]:
+ errormsg = (
+ "[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if metric != rib_routes_json[st_rt][0]["metric"]:
+ errormsg = (
+ "[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info(
+ "[DUT: {}]: Found next_hop {} for"
+ " RIB routes: {}".format(router, next_hop, found_routes)
+ )
+
+ if len(missing_routes) > 0:
+ errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+ dut, missing_routes
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
+
+ continue
+
+ if "bgp" in input_dict[routerInput]:
+ if (
+ "advertise_networks"
+ not in input_dict[routerInput]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]
+ ):
+ continue
+
+ found_routes = []
+ missing_routes = []
+ advertise_network = input_dict[routerInput]["bgp"]["address_family"][
+ addr_type
+ ]["unicast"]["advertise_networks"]
+
+ # Continue if there are no network advertise
+ if len(advertise_network) == 0:
+ continue
+
+ for advertise_network_dict in advertise_network:
+ if "vrf" in advertise_network_dict:
+ cmd = "{} vrf {} json".format(
+ command, advertise_network_dict["vrf"]
+ )
+ else:
+ cmd = "{} json".format(command)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) is False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+
+ start_ip = advertise_network_dict["network"]
+ if "no_of_network" in advertise_network_dict:
+ no_of_network = advertise_network_dict["no_of_network"]
+ else:
+ no_of_network = 1
+
+ # Generating IPs for verification
+ ip_list = generate_ips(start_ip, no_of_network)
+ st_found = False
+ nh_found = False
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt), strict=False))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if "queued" in rib_routes_json[st_rt][0]:
+ errormsg = "Route {} is queued\n".format(st_rt)
+ return errormsg
+
+ if next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ count = 0
+ for nh in next_hop:
+ for nh_dict in rib_routes_json[st_rt][0]["nexthops"]:
+ if nh_dict["ip"] != nh:
+ continue
+ else:
+ count += 1
+
+ if count == len(next_hop):
+ nh_found = True
+ else:
+ errormsg = (
+ "Nexthop {} is Missing"
+ " for route {} in "
+ "RIB of router {}\n".format(next_hop, st_rt, dut)
+ )
+ return errormsg
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info(
+ "Found next_hop {} for all routes in RIB"
+ " of router {}\n".format(next_hop, dut)
+ )
+
+ if len(missing_routes) > 0:
+ errormsg = (
+ "Missing {} route in RIB of router {}, "
+ "routes: {} \n".format(addr_type, dut, missing_routes)
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "Verified {} routes in router {} RIB, found"
+ " routes are: {}\n".format(addr_type, dut, found_routes)
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=12)
+def verify_fib_routes(tgen, addr_type, dut, input_dict, next_hop=None, protocol=None):
+ """
+ Data will be read from input_dict or input JSON file, API will generate
+ same prefixes, which were redistributed by either create_static_routes() or
+ advertise_networks_using_network_command() and will verify next_hop and
+ each prefix/routes is present in "show ip/ipv6 fib json"
+ command o/p.
+
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test, for which user wants to test the data
+ * `input_dict` : input dict, has details of static routes
+ * `next_hop`[optional]: next_hop which needs to be verified,
+ default: static
+
+ Usage
+ -----
+ input_routes_r1 = {
+ "r1": {
+ "static_routes": [{
+ "network": ["1.1.1.1/32],
+ "next_hop": "Null0",
+ "vrf": "RED"
+ }]
+ }
+ }
+ result = result = verify_fib_routes(tgen, "ipv4, "r1", input_routes_r1)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ if dut not in router_list:
+ return
+
+ for routerInput in input_dict.keys():
+ # XXX replace with router = dut; rnode = router_list[dut]
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s FIB routes:", router)
+
+ # Verifying RIB routes
+ if addr_type == "ipv4":
+ command = "show ip fib"
+ else:
+ command = "show ipv6 fib"
+
+ found_routes = []
+ missing_routes = []
+
+ if protocol:
+ command = "{} {}".format(command, protocol)
+
+ if "static_routes" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ if "vrf" in static_route and static_route["vrf"] is not None:
+
+ logger.info(
+ "[DUT: {}]: Verifying routes for VRF:"
+ " {}".format(router, static_route["vrf"])
+ )
+
+ cmd = "{} vrf {}".format(command, static_route["vrf"])
+
+ else:
+ cmd = "{}".format(command)
+
+ cmd = "{} json".format(cmd)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) is False:
+ errormsg = "[DUT: {}]: No route found in fib".format(router)
+ return errormsg
+
+ network = static_route["network"]
+ if "no_of_ip" in static_route:
+ no_of_ip = static_route["no_of_ip"]
+ else:
+ no_of_ip = 1
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+ st_found = False
+ nh_found = False
+
+ for st_rt in ip_list:
+ st_rt = str(
+ ipaddress.ip_network(frr_unicode(st_rt), strict=False)
+ )
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ count = 0
+ for nh in next_hop:
+ for nh_dict in rib_routes_json[st_rt][0][
+ "nexthops"
+ ]:
+ if nh_dict["ip"] != nh:
+ continue
+ else:
+ count += 1
+
+ if count == len(next_hop):
+ nh_found = True
+ else:
+ missing_routes.append(st_rt)
+ errormsg = (
+ "Nexthop {} is Missing"
+ " for route {} in "
+ "RIB of router {}\n".format(
+ next_hop, st_rt, dut
+ )
+ )
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if len(missing_routes) > 0:
+ errormsg = "[DUT: {}]: Missing route in FIB:" " {}".format(
+ dut, missing_routes
+ )
+ return errormsg
+
+ if nh_found:
+ logger.info(
+ "Found next_hop {} for all routes in RIB"
+ " of router {}\n".format(next_hop, dut)
+ )
+
+ if found_routes:
+ logger.info(
+ "[DUT: %s]: Verified routes in FIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
+
+ continue
+
+ if "bgp" in input_dict[routerInput]:
+ if (
+ "advertise_networks"
+ not in input_dict[routerInput]["bgp"]["address_family"][addr_type][
+ "unicast"
+ ]
+ ):
+ continue
+
+ found_routes = []
+ missing_routes = []
+ advertise_network = input_dict[routerInput]["bgp"]["address_family"][
+ addr_type
+ ]["unicast"]["advertise_networks"]
+
+ # Continue if there are no network advertise
+ if len(advertise_network) == 0:
+ continue
+
+ for advertise_network_dict in advertise_network:
+ if "vrf" in advertise_network_dict:
+ cmd = "{} vrf {} json".format(command, static_route["vrf"])
+ else:
+ cmd = "{} json".format(command)
+
+ rib_routes_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary rib_routes_json is not empty
+ if bool(rib_routes_json) is False:
+ errormsg = "No route found in rib of router {}..".format(router)
+ return errormsg
+
+ start_ip = advertise_network_dict["network"]
+ if "no_of_network" in advertise_network_dict:
+ no_of_network = advertise_network_dict["no_of_network"]
+ else:
+ no_of_network = 1
+
+ # Generating IPs for verification
+ ip_list = generate_ips(start_ip, no_of_network)
+ st_found = False
+ nh_found = False
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt), strict=False))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != addr_type:
+ continue
+
+ if st_rt in rib_routes_json:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ count = 0
+ for nh in next_hop:
+ for nh_dict in rib_routes_json[st_rt][0]["nexthops"]:
+ if nh_dict["ip"] != nh:
+ continue
+ else:
+ count += 1
+
+ if count == len(next_hop):
+ nh_found = True
+ else:
+ missing_routes.append(st_rt)
+ errormsg = (
+ "Nexthop {} is Missing"
+ " for route {} in "
+ "RIB of router {}\n".format(next_hop, st_rt, dut)
+ )
+ return errormsg
+ else:
+ missing_routes.append(st_rt)
+
+ if len(missing_routes) > 0:
+ errormsg = "[DUT: {}]: Missing route in FIB: " "{} \n".format(
+ dut, missing_routes
+ )
+ return errormsg
+
+ if nh_found:
+ logger.info(
+ "Found next_hop {} for all routes in RIB"
+ " of router {}\n".format(next_hop, dut)
+ )
+
+ if found_routes:
+ logger.info(
+ "[DUT: {}]: Verified routes FIB"
+ ", found routes are: {}\n".format(dut, found_routes)
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def verify_admin_distance_for_static_routes(tgen, input_dict):
+ """
+ API to verify admin distance for static routes as defined in input_dict/
+ input JSON by running show ip/ipv6 route json command.
+ Parameter
+ ---------
+ * `tgen` : topogen object
+ * `input_dict`: having details like - for which router and static routes
+ admin dsitance needs to be verified
+ Usage
+ -----
+ # To verify admin distance is 10 for prefix 10.0.20.1/32 having next_hop
+ 10.0.0.2 in router r1
+ input_dict = {
+ "r1": {
+ "static_routes": [{
+ "network": "10.0.20.1/32",
+ "admin_distance": 10,
+ "next_hop": "10.0.0.2"
+ }]
+ }
+ }
+ result = verify_admin_distance_for_static_routes(tgen, input_dict)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+ rnode = router_list[router]
+
+ for static_route in input_dict[router]["static_routes"]:
+ addr_type = validate_ip_address(static_route["network"])
+ # Command to execute
+ if addr_type == "ipv4":
+ command = "show ip route json"
+ else:
+ command = "show ipv6 route json"
+ show_ip_route_json = run_frr_cmd(rnode, command, isjson=True)
+
+ logger.info(
+ "Verifying admin distance for static route %s" " under dut %s:",
+ static_route,
+ router,
+ )
+ network = static_route["network"]
+ next_hop = static_route["next_hop"]
+ admin_distance = static_route["admin_distance"]
+ route_data = show_ip_route_json[network][0]
+ if network in show_ip_route_json:
+ if route_data["nexthops"][0]["ip"] == next_hop:
+ if route_data["distance"] != admin_distance:
+ errormsg = (
+ "Verification failed: admin distance"
+ " for static route {} under dut {},"
+ " found:{} but expected:{}".format(
+ static_route,
+ router,
+ route_data["distance"],
+ admin_distance,
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "Verification successful: admin"
+ " distance for static route %s under"
+ " dut %s, found:%s",
+ static_route,
+ router,
+ route_data["distance"],
+ )
+
+ else:
+ errormsg = (
+ "Static route {} not found in "
+ "show_ip_route_json for dut {}".format(network, router)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def verify_prefix_lists(tgen, input_dict):
+ """
+ Running "show ip prefix-list" command and verifying given prefix-list
+ is present in router.
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `input_dict`: data to verify prefix lists
+ Usage
+ -----
+ # To verify pf_list_1 is present in router r1
+ input_dict = {
+ "r1": {
+ "prefix_lists": ["pf_list_1"]
+ }}
+ result = verify_prefix_lists("ipv4", input_dict, tgen)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+
+ rnode = router_list[router]
+
+ # Show ip prefix list
+ show_prefix_list = run_frr_cmd(rnode, "show ip prefix-list")
+
+ # Verify Prefix list is deleted
+ prefix_lists_addr = input_dict[router]["prefix_lists"]
+ for addr_type in prefix_lists_addr:
+ if not check_address_types(addr_type):
+ continue
+ # show ip prefix list
+ if addr_type == "ipv4":
+ cmd = "show ip prefix-list"
+ else:
+ cmd = "show {} prefix-list".format(addr_type)
+ show_prefix_list = run_frr_cmd(rnode, cmd)
+ for prefix_list in prefix_lists_addr[addr_type].keys():
+ if prefix_list in show_prefix_list:
+ errormsg = (
+ "Prefix list {} is/are present in the router"
+ " {}".format(prefix_list, router)
+ )
+ return errormsg
+
+ logger.info(
+ "Prefix list %s is/are not present in the router" " from router %s",
+ prefix_list,
+ router,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=12)
+def verify_route_maps(tgen, input_dict):
+ """
+ Running "show route-map" command and verifying given route-map
+ is present in router.
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `input_dict`: data to verify prefix lists
+ Usage
+ -----
+ # To verify rmap_1 and rmap_2 are present in router r1
+ input_dict = {
+ "r1": {
+ "route_maps": ["rmap_1", "rmap_2"]
+ }
+ }
+ result = verify_route_maps(tgen, input_dict)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+
+ rnode = router_list[router]
+ # Show ip route-map
+ show_route_maps = rnode.vtysh_cmd("show route-map")
+
+ # Verify route-map is deleted
+ route_maps = input_dict[router]["route_maps"]
+ for route_map in route_maps:
+ if route_map in show_route_maps:
+ errormsg = "Route map {} is not deleted from router" " {}".format(
+ route_map, router
+ )
+ return errormsg
+
+ logger.info(
+ "Route map %s is/are deleted successfully from" " router %s",
+ route_maps,
+ router,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=16)
+def verify_bgp_community(tgen, addr_type, router, network, input_dict=None):
+ """
+ API to veiryf BGP large community is attached in route for any given
+ DUT by running "show bgp ipv4/6 {route address} json" command.
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `addr_type` : ip type, ipv4/ipv6
+ * `dut`: Device Under Test
+ * `network`: network for which set criteria needs to be verified
+ * `input_dict`: having details like - for which router, community and
+ values needs to be verified
+ Usage
+ -----
+ networks = ["200.50.2.0/32"]
+ input_dict = {
+ "largeCommunity": "2:1:1 2:2:2 2:3:3 2:4:4 2:5:5"
+ }
+ result = verify_bgp_community(tgen, "ipv4", dut, network, input_dict=None)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ router_list = tgen.routers()
+ if router not in router_list:
+ return False
+
+ rnode = router_list[router]
+
+ logger.debug(
+ "Verifying BGP community attributes on dut %s: for %s " "network %s",
+ router,
+ addr_type,
+ network,
+ )
+
+ for net in network:
+ cmd = "show bgp {} {} json".format(addr_type, net)
+ show_bgp_json = rnode.vtysh_cmd(cmd, isjson=True)
+ logger.info(show_bgp_json)
+ if "paths" not in show_bgp_json:
+ return "Prefix {} not found in BGP table of router: {}".format(net, router)
+
+ as_paths = show_bgp_json["paths"]
+ found = False
+ for i in range(len(as_paths)):
+ if (
+ "largeCommunity" in show_bgp_json["paths"][i]
+ or "community" in show_bgp_json["paths"][i]
+ ):
+ found = True
+ logger.info(
+ "Large Community attribute is found for route:" " %s in router: %s",
+ net,
+ router,
+ )
+ if input_dict is not None:
+ for criteria, comm_val in input_dict.items():
+ show_val = show_bgp_json["paths"][i][criteria]["string"]
+ if comm_val == show_val:
+ logger.info(
+ "Verifying BGP %s for prefix: %s"
+ " in router: %s, found expected"
+ " value: %s",
+ criteria,
+ net,
+ router,
+ comm_val,
+ )
+ else:
+ errormsg = (
+ "Failed: Verifying BGP attribute"
+ " {} for route: {} in router: {}"
+ ", expected value: {} but found"
+ ": {}".format(criteria, net, router, comm_val, show_val)
+ )
+ return errormsg
+
+ if not found:
+ errormsg = (
+ "Large Community attribute is not found for route: "
+ "{} in router: {} ".format(net, router)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def get_ipv6_linklocal_address(topo, node, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `node`: node on which link local ip to be fetched.
+ * `intf` : interface for which link local ip needs to be returned.
+ * `topo` : base topo
+
+ Usage
+ -----
+ result = get_ipv6_linklocal_address(topo, 'r1', 'r2')
+
+ Returns link local ip of interface between r1 and r2.
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface
+ 2) errormsg - when link local ip not found
+ """
+ tgen = get_topogen()
+ ext_nh = tgen.net[node].get_ipv6_linklocal()
+ req_nh = topo[node]["links"][intf]["interface"]
+ llip = None
+ for llips in ext_nh:
+ if llips[0] == req_nh:
+ llip = llips[1]
+ logger.info("Link local ip found = %s", llip)
+ return llip
+
+ errormsg = "Failed: Link local ip not found on router {}, " "interface {}".format(
+ node, intf
+ )
+
+ return errormsg
+
+
+def verify_create_community_list(tgen, input_dict):
+ """
+ API is to verify if large community list is created for any given DUT in
+ input_dict by running "sh bgp large-community-list {"comm_name"} detail"
+ command.
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict`: having details like - for which router, large community
+ needs to be verified
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "large-community-list": {
+ "standard": {
+ "Test1": [{"action": "PERMIT", "attribute":\
+ ""}]
+ }}}}
+ result = verify_create_community_list(tgen, input_dict)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+
+ rnode = router_list[router]
+
+ logger.info("Verifying large-community is created for dut %s:", router)
+
+ for comm_data in input_dict[router]["bgp_community_lists"]:
+ comm_name = comm_data["name"]
+ comm_type = comm_data["community_type"]
+ show_bgp_community = run_frr_cmd(
+ rnode, "show bgp large-community-list {} detail".format(comm_name)
+ )
+
+ # Verify community list and type
+ if comm_name in show_bgp_community and comm_type in show_bgp_community:
+ logger.info(
+ "BGP %s large-community-list %s is" " created", comm_type, comm_name
+ )
+ else:
+ errormsg = "BGP {} large-community-list {} is not" " created".format(
+ comm_type, comm_name
+ )
+ return errormsg
+
+ 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.gears[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(retry_timeout=12)
+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.gears[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(retry_timeout=12)
+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.gears[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
+
+
+def required_linux_kernel_version(required_version):
+ """
+ This API is used to check linux version compatibility of the test suite.
+ If version mentioned in required_version is higher than the linux kernel
+ of the system, test suite will be skipped. This API returns true or errormsg.
+
+ Parameters
+ ----------
+ * `required_version` : Kernel version required for the suites to run.
+
+ Usage
+ -----
+ result = linux_kernel_version_lowerthan('4.15')
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ system_kernel = platform.release()
+ if version_cmp(system_kernel, required_version) < 0:
+ error_msg = (
+ 'These tests will not run on kernel "{}", '
+ "they require kernel >= {})".format(system_kernel, required_version)
+ )
+
+ logger.info(error_msg)
+
+ return error_msg
+ return True
+
+
+class HostApplicationHelper(object):
+ """Helper to track and cleanup per-host based test processes."""
+
+ def __init__(self, tgen=None, base_cmd=None):
+ self.base_cmd_str = ""
+ self.host_procs = {}
+ self.tgen = None
+ self.set_base_cmd(base_cmd if base_cmd else [])
+ if tgen is not None:
+ self.init(tgen)
+
+ def __enter__(self):
+ self.init()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ self.cleanup()
+
+ def __str__(self):
+ return "HostApplicationHelper({})".format(self.base_cmd_str)
+
+ def set_base_cmd(self, base_cmd):
+ assert isinstance(base_cmd, list) or isinstance(base_cmd, tuple)
+ self.base_cmd = base_cmd
+ if base_cmd:
+ self.base_cmd_str = " ".join(base_cmd)
+ else:
+ self.base_cmd_str = ""
+
+ def init(self, tgen=None):
+ """Initialize the helper with tgen if needed.
+
+ If overridden, need to handle multiple entries but one init. Will be called on
+ object creation if tgen is supplied. Will be called again on __enter__ so should
+ not re-init if already inited.
+ """
+ if self.tgen:
+ assert tgen is None or self.tgen == tgen
+ else:
+ self.tgen = tgen
+
+ def started_proc(self, host, p):
+ """Called after process started on host.
+
+ Return value is passed to `stopping_proc` method."""
+ logger.debug("%s: Doing nothing after starting process", self)
+ return False
+
+ def stopping_proc(self, host, p, info):
+ """Called after process started on host."""
+ logger.debug("%s: Doing nothing before stopping process", self)
+
+ def _add_host_proc(self, host, p):
+ v = self.started_proc(host, p)
+
+ if host not in self.host_procs:
+ self.host_procs[host] = []
+ logger.debug("%s: %s: tracking process %s", self, host, p)
+ self.host_procs[host].append((p, v))
+
+ def stop_host(self, host):
+ """Stop the process on the host.
+
+ Override to do additional cleanup."""
+ if host in self.host_procs:
+ hlogger = self.tgen.net[host].logger
+ for p, v in self.host_procs[host]:
+ self.stopping_proc(host, p, v)
+ logger.debug("%s: %s: terminating process %s", self, host, p.pid)
+ hlogger.debug("%s: %s: terminating process %s", self, host, p.pid)
+ rc = p.poll()
+ if rc is not None:
+ logger.error(
+ "%s: %s: process early exit %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ hlogger.error(
+ "%s: %s: process early exit %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ else:
+ p.terminate()
+ p.wait()
+ logger.debug(
+ "%s: %s: terminated process %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+ hlogger.debug(
+ "%s: %s: terminated process %s: %s",
+ self,
+ host,
+ p.pid,
+ comm_error(p),
+ )
+
+ del self.host_procs[host]
+
+ def stop_all_hosts(self):
+ hosts = set(self.host_procs)
+ for host in hosts:
+ self.stop_host(host)
+
+ def cleanup(self):
+ self.stop_all_hosts()
+
+ def run(self, host, cmd_args, **kwargs):
+ cmd = list(self.base_cmd)
+ cmd.extend(cmd_args)
+ p = self.tgen.gears[host].popen(cmd, **kwargs)
+ assert p.poll() is None
+ self._add_host_proc(host, p)
+ return p
+
+ def check_procs(self):
+ """Check that all current processes are running, log errors if not.
+
+ Returns: List of stopped processes."""
+ procs = []
+
+ logger.debug("%s: checking procs on hosts %s", self, self.host_procs.keys())
+
+ for host in self.host_procs:
+ hlogger = self.tgen.net[host].logger
+ for p, _ in self.host_procs[host]:
+ logger.debug("%s: checking %s proc %s", self, host, p)
+ rc = p.poll()
+ if rc is None:
+ continue
+ logger.error(
+ "%s: %s proc exited: %s", self, host, comm_error(p), exc_info=True
+ )
+ hlogger.error(
+ "%s: %s proc exited: %s", self, host, comm_error(p), exc_info=True
+ )
+ procs.append(p)
+ return procs
+
+
+class IPerfHelper(HostApplicationHelper):
+ def __str__(self):
+ return "IPerfHelper()"
+
+ def run_join(
+ self,
+ host,
+ join_addr,
+ l4Type="UDP",
+ join_interval=1,
+ join_intf=None,
+ join_towards=None,
+ ):
+ """
+ Use iperf to send IGMP join and listen to traffic
+
+ Parameters:
+ -----------
+ * `host`: iperf host from where IGMP join would be sent
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `join_addr`: multicast address (or addresses) to join to
+ * `join_interval`: seconds between periodic bandwidth reports
+ * `join_intf`: the interface to bind the join to
+ * `join_towards`: router whos interface to bind the join to
+
+ returns: Success (bool)
+ """
+
+ iperf_path = self.tgen.net.get_exec_path("iperf")
+
+ assert join_addr
+ if not isinstance(join_addr, list) and not isinstance(join_addr, tuple):
+ join_addr = [ipaddress.IPv4Address(frr_unicode(join_addr))]
+
+ for bindTo in join_addr:
+ iperf_args = [iperf_path, "-s"]
+
+ if l4Type == "UDP":
+ iperf_args.append("-u")
+
+ iperf_args.append("-B")
+ if join_towards:
+ to_intf = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][join_towards][
+ "interface"
+ ]
+ )
+ iperf_args.append("{}%{}".format(str(bindTo), to_intf))
+ elif join_intf:
+ iperf_args.append("{}%{}".format(str(bindTo), join_intf))
+ else:
+ iperf_args.append(str(bindTo))
+
+ if join_interval:
+ iperf_args.append("-i")
+ iperf_args.append(str(join_interval))
+
+ p = self.run(host, iperf_args)
+ if p.poll() is not None:
+ logger.error("IGMP join failed on %s: %s", bindTo, comm_error(p))
+ return False
+ return True
+
+ def run_traffic(
+ self, host, sentToAddress, ttl, time=0, l4Type="UDP", bind_towards=None
+ ):
+ """
+ Run iperf to send IGMP join and traffic
+
+ Parameters:
+ -----------
+ * `host`: iperf host to send traffic from
+ * `l4Type`: string, one of [ TCP, UDP ]
+ * `sentToAddress`: multicast address to send traffic to
+ * `ttl`: time to live
+ * `time`: time in seconds to transmit for
+ * `bind_towards`: Router who's interface the source ip address is got from
+
+ returns: Success (bool)
+ """
+
+ iperf_path = self.tgen.net.get_exec_path("iperf")
+
+ if sentToAddress and not isinstance(sentToAddress, list):
+ sentToAddress = [ipaddress.IPv4Address(frr_unicode(sentToAddress))]
+
+ for sendTo in sentToAddress:
+ iperf_args = [iperf_path, "-c", sendTo]
+
+ # Bind to Interface IP
+ if bind_towards:
+ ifaddr = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][bind_towards]["ipv4"]
+ )
+ ipaddr = ipaddress.IPv4Interface(ifaddr).ip
+ iperf_args.append("-B")
+ iperf_args.append(str(ipaddr))
+
+ # UDP/TCP
+ if l4Type == "UDP":
+ iperf_args.append("-u")
+ iperf_args.append("-b")
+ iperf_args.append("0.012m")
+
+ # TTL
+ if ttl:
+ iperf_args.append("-T")
+ iperf_args.append(str(ttl))
+
+ # Time
+ if time:
+ iperf_args.append("-t")
+ iperf_args.append(str(time))
+
+ p = self.run(host, iperf_args)
+ if p.poll() is not None:
+ logger.error(
+ "mcast traffic send failed for %s: %s", sendTo, comm_error(p)
+ )
+ return False
+
+ return True
+
+
+def verify_ip_nht(tgen, input_dict):
+ """
+ Running "show ip nht" command and verifying given nexthop resolution
+ Parameters
+ ----------
+ * `tgen` : topogen object
+ * `input_dict`: data to verify nexthop
+ Usage
+ -----
+ input_dict_4 = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {
+ "nexthop1": {
+ "Interface": intf
+ }
+ }
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_4)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: verify_ip_nht()")
+
+ router_list = tgen.routers()
+ for router in input_dict.keys():
+ if router not in router_list:
+ continue
+
+ rnode = router_list[router]
+ nh_list = input_dict[router]
+
+ if validate_ip_address(next(iter(nh_list))) == "ipv6":
+ show_ip_nht = run_frr_cmd(rnode, "show ipv6 nht")
+ else:
+ show_ip_nht = run_frr_cmd(rnode, "show ip nht")
+
+ for nh in nh_list:
+ if nh in show_ip_nht:
+ nht = run_frr_cmd(rnode, "show ip nht {}".format(nh))
+ if "unresolved" in nht:
+ errormsg = "Nexthop {} became unresolved on {}".format(nh, router)
+ return errormsg
+ else:
+ logger.info("Nexthop %s is resolved on %s", nh, router)
+ return True
+ else:
+ errormsg = "Nexthop {} is resolved on {}".format(nh, router)
+ return errormsg
+
+ logger.debug("Exiting lib API: verify_ip_nht()")
+ return False
+
+
+def scapy_send_raw_packet(tgen, topo, senderRouter, intf, packet=None):
+ """
+ Using scapy Raw() method to send BSR raw packet from one FRR
+ to other
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `senderRouter` : Sender router
+ * `packet` : packet in raw format
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ global CD
+ result = ""
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ sender_interface = intf
+ rnode = tgen.routers()[senderRouter]
+
+ for destLink, data in topo["routers"][senderRouter]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if not packet:
+ packet = topo["routers"][senderRouter]["pkt"]["test_packets"][packet][
+ "data"
+ ]
+
+ python3_path = tgen.net.get_exec_path(["python3", "python"])
+ script_path = os.path.join(CD, "send_bsr_packet.py")
+ cmd = "{} {} '{}' '{}' --interval=1 --count=1".format(
+ python3_path, script_path, packet, sender_interface
+ )
+
+ logger.info("Scapy cmd: \n %s", cmd)
+ result = rnode.run(cmd)
+
+ if result == "":
+ return result
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
diff --git a/tests/topotests/lib/exa-receive.py b/tests/topotests/lib/exa-receive.py
new file mode 100755
index 0000000..2ea3a75
--- /dev/null
+++ b/tests/topotests/lib/exa-receive.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python3
+
+"""
+exa-receive.py: Save received routes form ExaBGP into file
+"""
+
+import argparse
+import os
+from sys import stdin
+from datetime import datetime
+
+parser = argparse.ArgumentParser()
+parser.add_argument(
+ "--no-timestamp", dest="timestamp", action="store_false", help="Disable timestamps"
+)
+parser.add_argument(
+ "--logdir", default="/tmp/gearlogdir", help="The directory to store the peer log in"
+)
+parser.add_argument("peer", type=int, help="The peer number")
+args = parser.parse_args()
+
+savepath = os.path.join(args.logdir, "peer{}-received.log".format(args.peer))
+routesavefile = open(savepath, "w")
+
+while True:
+ try:
+ line = stdin.readline()
+ if not line:
+ break
+
+ if not args.timestamp:
+ routesavefile.write(line)
+ else:
+ timestamp = datetime.now().strftime("%Y%m%d_%H:%M:%S - ")
+ routesavefile.write(timestamp + line)
+ routesavefile.flush()
+ except KeyboardInterrupt:
+ pass
+ except IOError:
+ # most likely a signal during readline
+ pass
+
+routesavefile.close()
diff --git a/tests/topotests/lib/fixtures.py b/tests/topotests/lib/fixtures.py
new file mode 100644
index 0000000..9d8f63a
--- /dev/null
+++ b/tests/topotests/lib/fixtures.py
@@ -0,0 +1,47 @@
+# -*- coding: utf-8 eval: (yapf-mode 1) -*-
+#
+# August 27 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C. ("LabN")
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import lib.topojson as topojson
+import lib.topogen as topogen
+from lib.topolog import logger
+
+
+def tgen_json(request):
+ logger.info("Creating/starting topogen topology for %s", request.module.__name__)
+
+ tgen = topojson.setup_module_from_json(request.module.__file__)
+ yield tgen
+
+ logger.info("Stopping topogen topology for %s", request.module.__name__)
+ tgen.stop_topology()
+
+
+def topo(tgen):
+ """Make tgen json object available as test argument."""
+ return tgen.json_topo
+
+
+def tgen():
+ """Make global topogen object available as test argument."""
+ return topogen.get_topogen()
diff --git a/tests/topotests/lib/grpc-query.py b/tests/topotests/lib/grpc-query.py
new file mode 100755
index 0000000..61f01c3
--- /dev/null
+++ b/tests/topotests/lib/grpc-query.py
@@ -0,0 +1,155 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# February 22 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2022, LabN Consulting, L.L.C.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import argparse
+import logging
+import os
+import sys
+
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+# This is painful but works if you have installed grpc and grpc_tools would be *way*
+# better if we actually built and installed these but ... python packaging.
+try:
+ import grpc
+ import grpc_tools
+
+ from micronet import commander
+
+ commander.cmd_raises(f"cp {CWD}/../../../grpc/frr-northbound.proto .")
+ commander.cmd_raises(
+ f"python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I . frr-northbound.proto"
+ )
+except Exception as error:
+ logging.error("can't create proto definition modules %s", error)
+ raise
+
+try:
+ sys.path[0:0] = "."
+ import frr_northbound_pb2
+ import frr_northbound_pb2_grpc
+
+ # Would be nice if compiling the modules internally from the source worked
+ # # import grpc_tools.protoc
+ # # proto_include = pkg_resources.resource_filename("grpc_tools", "_proto")
+ # from grpc_tools.protoc import _proto_file_to_module_name, _protos_and_services
+ # try:
+ # frr_northbound_pb2, frr_northbound_pb2_grpc = _protos_and_services(
+ # "frr_northbound.proto"
+ # )
+ # finally:
+ # os.chdir(CWD)
+except Exception as error:
+ logging.error("can't import proto definition modules %s", error)
+ raise
+
+
+class GRPCClient:
+ def __init__(self, server, port):
+ self.channel = grpc.insecure_channel("{}:{}".format(server, port))
+ self.stub = frr_northbound_pb2_grpc.NorthboundStub(self.channel)
+
+ def get_capabilities(self):
+ request = frr_northbound_pb2.GetCapabilitiesRequest()
+ response = "NONE"
+ try:
+ response = self.stub.GetCapabilities(request)
+ except Exception as error:
+ logging.error("Got exception from stub: %s", error)
+
+ logging.debug("GRPC Capabilities: %s", response)
+ return response
+
+ def get(self, xpath):
+ request = frr_northbound_pb2.GetRequest()
+ request.path.append(xpath)
+ request.type = frr_northbound_pb2.GetRequest.ALL
+ request.encoding = frr_northbound_pb2.XML
+ xml = ""
+ for r in self.stub.Get(request):
+ logging.info('GRPC Get path: "%s" value: %s', request.path, r)
+ xml += str(r.data.data)
+ return xml
+
+
+def next_action(action_list=None):
+ "Get next action from list or STDIN"
+ if action_list:
+ for action in action_list:
+ yield action
+ else:
+ while True:
+ try:
+ action = input("")
+ if not action:
+ break
+ yield action.strip()
+ except EOFError:
+ break
+
+
+def main(*args):
+ parser = argparse.ArgumentParser(description="gRPC Client")
+ parser.add_argument(
+ "-s", "--server", default="localhost", help="gRPC Server Address"
+ )
+ parser.add_argument(
+ "-p", "--port", type=int, default=50051, help="gRPC Server TCP Port"
+ )
+ parser.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ parser.add_argument("--check", action="store_true", help="check runable")
+ parser.add_argument("actions", nargs="*", help="GETCAP|GET,xpath")
+ args = parser.parse_args(*args)
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level,
+ format="%(asctime)s %(levelname)s: GRPC-CLI-CLIENT: %(name)s %(message)s",
+ )
+
+ if args.check:
+ sys.exit(0)
+
+ c = GRPCClient(args.server, args.port)
+
+ for action in next_action(args.actions):
+ action = action.casefold()
+ logging.info("GOT ACTION: %s", action)
+ if action == "getcap":
+ caps = c.get_capabilities()
+ print("Capabilities:", caps)
+ elif action.startswith("get,"):
+ # Print Interface State and Config
+ _, xpath = action.split(",", 1)
+ print("Get XPath: ", xpath)
+ xml = c.get(xpath)
+ print("{}: {}".format(xpath, xml))
+ # for _ in range(0, 1):
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/lib/ltemplate.py b/tests/topotests/lib/ltemplate.py
new file mode 100644
index 0000000..2544023
--- /dev/null
+++ b/tests/topotests/lib/ltemplate.py
@@ -0,0 +1,320 @@
+#!/usr/bin/env python
+
+#
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+ltemplate.py: LabN template for FRR tests.
+"""
+
+import os
+import sys
+import platform
+
+import pytest
+
+# 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.lutil import *
+
+# Required to instantiate the topology builder class.
+
+customize = None
+
+
+class LTemplate:
+ test = None
+ testdir = None
+ scriptdir = None
+ logdir = None
+ prestarthooksuccess = True
+ poststarthooksuccess = True
+ iproute2Ver = None
+
+ def __init__(self, test, testdir):
+ pathname = os.path.join(testdir, "customize.py")
+ global customize
+ if sys.version_info >= (3, 5):
+ import importlib.util
+
+ spec = importlib.util.spec_from_file_location("customize", pathname)
+ customize = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(customize)
+ else:
+ import imp
+
+ customize = imp.load_source("customize", pathname)
+ self.test = test
+ self.testdir = testdir
+ self.scriptdir = testdir
+ self.logdir = ""
+ logger.info("LTemplate: " + test)
+
+ def setup_module(self, mod):
+ "Sets up the pytest environment"
+ # This function initiates the topology build with Topogen...
+ tgen = Topogen(customize.build_topo, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ self.logdir = tgen.logdir
+
+ logger.info("Topology started")
+ try:
+ self.prestarthooksuccess = customize.ltemplatePreRouterStartHook()
+ except AttributeError:
+ # not defined
+ logger.debug("ltemplatePreRouterStartHook() not defined")
+ if self.prestarthooksuccess != True:
+ logger.info("ltemplatePreRouterStartHook() failed, skipping test")
+ return
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ logger.info("Setting up %s" % rname)
+ for rd_val in TopoRouter.RD:
+ config = os.path.join(
+ self.testdir, "{}/{}.conf".format(rname, TopoRouter.RD[rd_val])
+ )
+ prog = os.path.join(tgen.net[rname].daemondir, TopoRouter.RD[rd_val])
+ if os.path.exists(config):
+ if os.path.exists(prog):
+ router.load_config(rd_val, config)
+ else:
+ logger.warning(
+ "{} not found, but have {}.conf file".format(
+ prog, TopoRouter.RD[rd_val]
+ )
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ logger.info("Starting routers")
+ tgen.start_router()
+ try:
+ self.poststarthooksuccess = customize.ltemplatePostRouterStartHook()
+ except AttributeError:
+ # not defined
+ logger.debug("ltemplatePostRouterStartHook() not defined")
+ luStart(baseScriptDir=self.scriptdir, baseLogDir=self.logdir, net=tgen.net)
+
+
+# initialized by ltemplate_start
+_lt = None
+
+
+def setup_module(mod):
+ global _lt
+ root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ test = mod.__name__[: mod.__name__.rfind(".")]
+ testdir = os.path.join(root, test)
+
+ # don't do this for now as reload didn't work as expected
+ # fixup sys.path, want test dir there only once
+ # try:
+ # sys.path.remove(testdir)
+ # except ValueError:
+ # logger.debug(testdir+" not found in original sys.path")
+ # add testdir
+ # sys.path.append(testdir)
+
+ # init class
+ _lt = LTemplate(test, testdir)
+ _lt.setup_module(mod)
+
+ # drop testdir
+ # sys.path.remove(testdir)
+
+
+def teardown_module(mod):
+ global _lt
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ if _lt != None and _lt.scriptdir != None and _lt.prestarthooksuccess == True:
+ luShowResults(logger.info)
+ print(luFinish())
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+ _lt = None
+
+
+def ltemplateTest(
+ script, SkipIfFailed=True, CallOnFail=None, CheckFuncStr=None, KeepGoing=False
+):
+ global _lt
+ if _lt == None or _lt.prestarthooksuccess != True:
+ return
+
+ tgen = get_topogen()
+ if not os.path.isfile(script):
+ if not os.path.isfile(os.path.join(_lt.scriptdir, script)):
+ logger.error("Could not find script file: " + script)
+ assert "Could not find script file: " + script
+ logger.info("Starting template test: " + script)
+ numEntry = luNumFail()
+
+ if SkipIfFailed and tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ if numEntry > 0:
+ if not KeepGoing:
+ pytest.skip("Have %d errors" % numEntry)
+
+ if CheckFuncStr != None:
+ check = eval(CheckFuncStr)
+ if check != True:
+ pytest.skip("Check function '" + CheckFuncStr + "' returned: " + check)
+
+ if CallOnFail != None:
+ CallOnFail = eval(CallOnFail)
+ luInclude(script, CallOnFail)
+ numFail = luNumFail() - numEntry
+ if numFail > 0:
+ luShowFail()
+ fatal_error = "%d tests failed" % numFail
+ if not KeepGoing:
+ assert (
+ "scripts/cleanup_all.py failed" == "See summary output above"
+ ), fatal_error
+
+
+# Memory leak test template
+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()
+
+
+class ltemplateRtrCmd:
+ def __init__(self):
+ self.resetCounts()
+
+ def doCmd(self, tgen, rtr, cmd, checkstr=None):
+ logger.info("doCmd: {} {}".format(rtr, cmd))
+ output = tgen.net[rtr].cmd(cmd).strip()
+ if len(output):
+ self.output += 1
+ if checkstr != None:
+ ret = re.search(checkstr, output)
+ if ret == None:
+ self.nomatch += 1
+ else:
+ self.match += 1
+ return ret
+ logger.info("output: " + output)
+ else:
+ logger.info("No output")
+ self.none += 1
+ return None
+
+ def resetCounts(self):
+ self.match = 0
+ self.nomatch = 0
+ self.output = 0
+ self.none = 0
+
+ def getMatch(self):
+ return self.match
+
+ def getNoMatch(self):
+ return self.nomatch
+
+ def getOutput(self):
+ return self.output
+
+ def getNone(self):
+ return self.none
+
+
+def ltemplateVersionCheck(
+ vstr, rname="r1", compstr="<", cli=False, kernel="4.9", iproute2=None, mpls=True
+):
+ tgen = get_topogen()
+ router = tgen.gears[rname]
+
+ if cli:
+ logger.info("calling mininet CLI")
+ tgen.mininet_cli()
+ logger.info("exited mininet CLI")
+
+ if _lt == None:
+ ret = "Template not initialized"
+ return ret
+
+ if _lt.prestarthooksuccess != True:
+ ret = "ltemplatePreRouterStartHook failed"
+ return ret
+
+ if _lt.poststarthooksuccess != True:
+ ret = "ltemplatePostRouterStartHook failed"
+ return ret
+
+ if mpls == True and tgen.hasmpls != True:
+ ret = "MPLS not initialized"
+ return ret
+
+ if kernel != None:
+ krel = platform.release()
+ if topotest.version_cmp(krel, kernel) < 0:
+ ret = "Skipping tests, old kernel ({} < {})".format(krel, kernel)
+ return ret
+
+ if iproute2 != None:
+ if _lt.iproute2Ver == None:
+ # collect/log info on iproute2
+ cc = ltemplateRtrCmd()
+ found = cc.doCmd(
+ tgen, rname, "apt-cache policy iproute2", r"Installed: ([\d\.]*)"
+ )
+ if found != None:
+ iproute2Ver = found.group(1)
+ else:
+ iproute2Ver = "0-unknown"
+ logger.info("Have iproute2 version=" + iproute2Ver)
+
+ if topotest.version_cmp(iproute2Ver, iproute2) < 0:
+ ret = "Skipping tests, old iproute2 ({} < {})".format(iproute2Ver, iproute2)
+ return ret
+
+ ret = True
+ try:
+ if router.has_version(compstr, vstr):
+ ret = "Skipping tests, old FRR version {} {}".format(compstr, vstr)
+ return ret
+ except:
+ ret = True
+
+ return ret
+
+
+# for testing
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/lib/lutil.py b/tests/topotests/lib/lutil.py
new file mode 100644
index 0000000..5c1fa24
--- /dev/null
+++ b/tests/topotests/lib/lutil.py
@@ -0,0 +1,465 @@
+#!/usr/bin/env python
+
+# Copyright 2017, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import os
+import re
+import sys
+import time
+import json
+import math
+import time
+from lib.topolog import logger
+from lib.topotest import json_cmp
+
+
+# L utility functions
+#
+# These functions are inteneted to provide support for CI testing within MiniNet
+# environments.
+
+
+class lUtil:
+ # to be made configurable in the future
+ base_script_dir = "."
+ base_log_dir = "."
+ fout_name = "output.log"
+ fsum_name = "summary.txt"
+ l_level = 6
+ CallOnFail = False
+
+ l_total = 0
+ l_pass = 0
+ l_fail = 0
+ l_filename = ""
+ l_last = None
+ l_line = 0
+ l_dotall_experiment = False
+ l_last_nl = None
+
+ fout = ""
+ fsum = ""
+ net = ""
+
+ def log(self, str, level=6):
+ if self.l_level > 0:
+ if self.fout == "":
+ self.fout = open(self.fout_name, "w")
+ self.fout.write(str + "\n")
+ if level <= self.l_level:
+ print(str)
+
+ def summary(self, str):
+ if self.fsum == "":
+ self.fsum = open(self.fsum_name, "w")
+ self.fsum.write(
+ "\
+******************************************************************************\n"
+ )
+ self.fsum.write(
+ "\
+Test Target Summary Pass Fail\n"
+ )
+ self.fsum.write(
+ "\
+******************************************************************************\n"
+ )
+ self.fsum.write(str + "\n")
+
+ def result(self, target, success, str, logstr=None):
+ if success:
+ p = 1
+ f = 0
+ self.l_pass += 1
+ sstr = "PASS"
+ else:
+ f = 1
+ p = 0
+ self.l_fail += 1
+ sstr = "FAIL"
+ self.l_total += 1
+ if logstr != None:
+ self.log("R:%d %s: %s" % (self.l_total, sstr, logstr))
+ res = "%-4d %-6s %-56s %-4d %d" % (self.l_total, target, str, p, f)
+ self.log("R:" + res)
+ self.summary(res)
+ if f == 1 and self.CallOnFail != False:
+ self.CallOnFail()
+
+ def closeFiles(self):
+ ret = (
+ "\
+******************************************************************************\n\
+Total %-4d %-4d %d\n\
+******************************************************************************"
+ % (self.l_total, self.l_pass, self.l_fail)
+ )
+ if self.fsum != "":
+ self.fsum.write(ret + "\n")
+ self.fsum.close()
+ self.fsum = ""
+ if self.fout != "":
+ if os.path.isfile(self.fsum_name):
+ r = open(self.fsum_name, "r")
+ self.fout.write(r.read())
+ r.close()
+ self.fout.close()
+ self.fout = ""
+ return ret
+
+ def setFilename(self, name):
+ str = "FILE: " + name
+ self.log(str)
+ self.summary(str)
+ self.l_filename = name
+ self.line = 0
+
+ def getCallOnFail(self):
+ return self.CallOnFail
+
+ def setCallOnFail(self, CallOnFail):
+ self.CallOnFail = CallOnFail
+
+ def strToArray(self, string):
+ a = []
+ c = 0
+ end = ""
+ words = string.split()
+ if len(words) < 1 or words[0].startswith("#"):
+ return a
+ words = string.split()
+ for word in words:
+ if len(end) == 0:
+ a.append(word)
+ else:
+ a[c] += str(" " + word)
+ if end == "\\":
+ end = ""
+ if not word.endswith("\\"):
+ if end != '"':
+ if word.startswith('"'):
+ end = '"'
+ else:
+ c += 1
+ else:
+ if word.endswith('"'):
+ end = ""
+ c += 1
+ else:
+ c += 1
+ else:
+ end = "\\"
+ # if len(end) == 0:
+ # print('%d:%s:' % (c, a[c-1]))
+
+ return a
+
+ def execTestFile(self, tstFile):
+ if os.path.isfile(tstFile):
+ f = open(tstFile)
+ for line in f:
+ if len(line) > 1:
+ a = self.strToArray(line)
+ if len(a) >= 6:
+ luCommand(a[1], a[2], a[3], a[4], a[5])
+ else:
+ self.l_line += 1
+ self.log("%s:%s %s" % (self.l_filename, self.l_line, line))
+ if len(a) >= 2:
+ if a[0] == "sleep":
+ time.sleep(int(a[1]))
+ elif a[0] == "include":
+ self.execTestFile(a[1])
+ f.close()
+ else:
+ self.log("unable to read: " + tstFile)
+ sys.exit(1)
+
+ def command(self, target, command, regexp, op, result, returnJson, startt=None):
+ global net
+ if op == "jsoncmp_pass" or op == "jsoncmp_fail":
+ returnJson = True
+
+ self.log(
+ "%s (#%d) %s:%s COMMAND:%s:%s:%s:%s:%s:"
+ % (
+ time.asctime(),
+ self.l_total + 1,
+ self.l_filename,
+ self.l_line,
+ target,
+ command,
+ regexp,
+ op,
+ result,
+ )
+ )
+ if self.net == "":
+ return False
+ # self.log("Running %s %s" % (target, command))
+ js = None
+ out = self.net[target].cmd(command).rstrip()
+ if len(out) == 0:
+ report = "<no output>"
+ else:
+ report = out
+ if returnJson == True:
+ try:
+ js = json.loads(out)
+ except:
+ js = None
+ self.log(
+ "WARNING: JSON load failed -- confirm command output is in JSON format."
+ )
+ self.log("COMMAND OUTPUT:%s:" % report)
+
+ # JSON comparison
+ if op == "jsoncmp_pass" or op == "jsoncmp_fail":
+ try:
+ expect = json.loads(regexp)
+ except:
+ expect = None
+ self.log(
+ "WARNING: JSON load failed -- confirm regex input is in JSON format."
+ )
+ json_diff = json_cmp(js, expect)
+ if json_diff != None:
+ if op == "jsoncmp_fail":
+ success = True
+ else:
+ success = False
+ self.log("JSON DIFF:%s:" % json_diff)
+ ret = success
+ else:
+ if op == "jsoncmp_fail":
+ success = False
+ else:
+ success = True
+ self.result(target, success, result)
+ if js != None:
+ return js
+ return ret
+
+ # Experiment: can we achieve the same match behavior via DOTALL
+ # without converting newlines to spaces?
+ out_nl = out
+ search_nl = re.search(regexp, out_nl, re.DOTALL)
+ self.l_last_nl = search_nl
+ # Set up for comparison
+ if search_nl != None:
+ group_nl = search_nl.group()
+ group_nl_converted = " ".join(group_nl.splitlines())
+ else:
+ group_nl_converted = None
+
+ out = " ".join(out.splitlines())
+ search = re.search(regexp, out)
+ self.l_last = search
+ if search == None:
+ if op == "fail":
+ success = True
+ else:
+ success = False
+ ret = success
+ else:
+ ret = search.group()
+ if op != "fail":
+ success = True
+ level = 7
+ else:
+ success = False
+ level = 5
+ self.log("found:%s:" % ret, level)
+ # Experiment: compare matched strings obtained each way
+ if self.l_dotall_experiment and (group_nl_converted != ret):
+ self.log(
+ "DOTALL experiment: strings differ dotall=[%s] orig=[%s]"
+ % (group_nl_converted, ret),
+ 9,
+ )
+ if startt != None:
+ if js != None or ret is not False:
+ delta = time.time() - startt
+ self.result(target, success, "%s +%4.2f secs" % (result, delta))
+ elif op == "pass" or op == "fail":
+ self.result(target, success, result)
+ if js != None:
+ return js
+ return ret
+
+ def wait(
+ self, target, command, regexp, op, result, wait, returnJson, wait_time=0.5
+ ):
+ self.log(
+ "%s:%s WAIT:%s:%s:%s:%s:%s:%s:%s:"
+ % (
+ self.l_filename,
+ self.l_line,
+ target,
+ command,
+ regexp,
+ op,
+ result,
+ wait,
+ wait_time,
+ )
+ )
+ found = False
+ n = 0
+ startt = time.time()
+
+ # Calculate the amount of `sleep`s we are going to peform.
+ wait_count = int(math.ceil(wait / wait_time)) + 1
+
+ while wait_count > 0:
+ n += 1
+ found = self.command(target, command, regexp, op, result, returnJson, startt)
+ if found is not False:
+ break
+
+ wait_count -= 1
+ if wait_count > 0:
+ time.sleep(wait_time)
+
+ delta = time.time() - startt
+ self.log("Done after %d loops, time=%s, Found=%s" % (n, delta, found))
+ return found
+
+
+# initialized by luStart
+LUtil = None
+
+# entry calls
+def luStart(
+ baseScriptDir=".",
+ baseLogDir=".",
+ net="",
+ fout="output.log",
+ fsum="summary.txt",
+ level=None,
+):
+ global LUtil
+ # init class
+ LUtil = lUtil()
+ LUtil.base_script_dir = baseScriptDir
+ LUtil.base_log_dir = baseLogDir
+ LUtil.net = net
+ if fout != "":
+ LUtil.fout_name = baseLogDir + "/" + fout
+ if fsum != None:
+ LUtil.fsum_name = baseLogDir + "/" + fsum
+ if level != None:
+ LUtil.l_level = level
+ LUtil.l_dotall_experiment = False
+ LUtil.l_dotall_experiment = True
+
+
+def luCommand(
+ target,
+ command,
+ regexp=".",
+ op="none",
+ result="",
+ time=10,
+ returnJson=False,
+ wait_time=0.5,
+):
+ if op != "wait":
+ return LUtil.command(target, command, regexp, op, result, returnJson)
+ else:
+ return LUtil.wait(
+ target, command, regexp, op, result, time, returnJson, wait_time
+ )
+
+
+def luLast(usenl=False):
+ if usenl:
+ if LUtil.l_last_nl != None:
+ LUtil.log("luLast:%s:" % LUtil.l_last_nl.group(), 7)
+ return LUtil.l_last_nl
+ else:
+ if LUtil.l_last != None:
+ LUtil.log("luLast:%s:" % LUtil.l_last.group(), 7)
+ return LUtil.l_last
+
+
+def luInclude(filename, CallOnFail=None):
+ tstFile = LUtil.base_script_dir + "/" + filename
+ LUtil.setFilename(filename)
+ if CallOnFail != None:
+ oldCallOnFail = LUtil.getCallOnFail()
+ LUtil.setCallOnFail(CallOnFail)
+ if filename.endswith(".py"):
+ LUtil.log("luInclude: execfile " + tstFile)
+ with open(tstFile) as infile:
+ exec(infile.read())
+ else:
+ LUtil.log("luInclude: execTestFile " + tstFile)
+ LUtil.execTestFile(tstFile)
+ if CallOnFail != None:
+ LUtil.setCallOnFail(oldCallOnFail)
+
+
+def luFinish():
+ global LUtil
+ ret = LUtil.closeFiles()
+ # done
+ LUtil = None
+ return ret
+
+
+def luNumFail():
+ return LUtil.l_fail
+
+
+def luNumPass():
+ return LUtil.l_pass
+
+
+def luResult(target, success, str, logstr=None):
+ return LUtil.result(target, success, str, logstr)
+
+
+def luShowResults(prFunction):
+ printed = 0
+ sf = open(LUtil.fsum_name, "r")
+ for line in sf:
+ printed += 1
+ prFunction(line.rstrip())
+ sf.close()
+
+
+def luShowFail():
+ printed = 0
+ sf = open(LUtil.fsum_name, "r")
+ for line in sf:
+ if line[-2] != "0":
+ printed += 1
+ logger.error(line.rstrip())
+ sf.close()
+ if printed > 0:
+ logger.error("See %s for details of errors" % LUtil.fout_name)
+
+
+# for testing
+if __name__ == "__main__":
+ print(os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + "/lib")
+ luStart()
+ for arg in sys.argv[1:]:
+ luInclude(arg)
+ luFinish()
+ sys.exit(0)
diff --git a/tests/topotests/lib/mcast-tester.py b/tests/topotests/lib/mcast-tester.py
new file mode 100755
index 0000000..30beccb
--- /dev/null
+++ b/tests/topotests/lib/mcast-tester.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2021 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 THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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.
+
+"""
+Subscribe to a multicast group so that the kernel sends an IGMP JOIN
+for the multicast group we subscribed to.
+"""
+
+import argparse
+import json
+import os
+import socket
+import struct
+import subprocess
+import sys
+import time
+
+
+#
+# Functions
+#
+def interface_name_to_index(name):
+ "Gets the interface index using its name. Returns None on failure."
+ interfaces = json.loads(subprocess.check_output("ip -j link show", shell=True))
+
+ for interface in interfaces:
+ if interface["ifname"] == name:
+ return interface["ifindex"]
+
+ return None
+
+
+def multicast_join(sock, ifindex, group, port):
+ "Joins a multicast group."
+ mreq = struct.pack(
+ "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex
+ )
+
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind((group, port))
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+
+
+#
+# Main code.
+#
+parser = argparse.ArgumentParser(description="Multicast RX utility")
+parser.add_argument("group", help="Multicast IP")
+parser.add_argument("interface", help="Interface name")
+parser.add_argument("--socket", help="Point to topotest UNIX socket")
+parser.add_argument(
+ "--send", help="Transmit instead of join with interval", type=float, default=0
+)
+args = parser.parse_args()
+
+ttl = 16
+port = 1000
+
+# Get interface index/validate.
+ifindex = interface_name_to_index(args.interface)
+if ifindex is None:
+ sys.stderr.write("Interface {} does not exists\n".format(args.interface))
+ sys.exit(1)
+
+# We need root privileges to set up multicast.
+if os.geteuid() != 0:
+ sys.stderr.write("ERROR: You must have root privileges\n")
+ sys.exit(1)
+
+# Wait for topotest to synchronize with us.
+if not args.socket:
+ toposock = None
+else:
+ toposock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ while True:
+ try:
+ toposock.connect(args.socket)
+ break
+ except ConnectionRefusedError:
+ time.sleep(1)
+ continue
+ # Set topotest socket non blocking so we can multiplex the main loop.
+ toposock.setblocking(False)
+
+msock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+if args.send > 0:
+ # Prepare multicast bit in that interface.
+ msock.setsockopt(
+ socket.SOL_SOCKET,
+ 25,
+ struct.pack("%ds" % len(args.interface), args.interface.encode("utf-8")),
+ )
+ # Set packets TTL.
+ msock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", ttl))
+ # Block to ensure packet send.
+ msock.setblocking(True)
+else:
+ multicast_join(msock, ifindex, args.group, port)
+
+
+def should_exit():
+ if not toposock:
+ # If we are sending then we have slept
+ if not args.send:
+ time.sleep(100)
+ return False
+ else:
+ try:
+ data = toposock.recv(1)
+ if data == b"":
+ print(" -> Connection closed")
+ return True
+ except BlockingIOError:
+ return False
+
+
+counter = 0
+while not should_exit():
+ if args.send > 0:
+ msock.sendto(b"test %d" % counter, (args.group, port))
+ counter += 1
+ time.sleep(args.send)
+
+msock.close()
+sys.exit(0)
diff --git a/tests/topotests/lib/micronet.py b/tests/topotests/lib/micronet.py
new file mode 100644
index 0000000..dfa10cc
--- /dev/null
+++ b/tests/topotests/lib/micronet.py
@@ -0,0 +1,1005 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 9 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+import datetime
+import logging
+import os
+import re
+import shlex
+import subprocess
+import sys
+import tempfile
+import time as time_mod
+import traceback
+
+root_hostname = subprocess.check_output("hostname")
+
+# This allows us to cleanup any leftovers later on
+os.environ["MICRONET_PID"] = str(os.getpid())
+
+
+class Timeout(object):
+ def __init__(self, delta):
+ self.started_on = datetime.datetime.now()
+ self.expires_on = self.started_on + datetime.timedelta(seconds=delta)
+
+ def elapsed(self):
+ elapsed = datetime.datetime.now() - self.started_on
+ return elapsed.total_seconds()
+
+ def is_expired(self):
+ return datetime.datetime.now() > self.expires_on
+
+
+def is_string(value):
+ """Return True if value is a string."""
+ try:
+ return isinstance(value, basestring) # type: ignore
+ except NameError:
+ return isinstance(value, str)
+
+
+def shell_quote(command):
+ """Return command wrapped in single quotes."""
+ if sys.version_info[0] >= 3:
+ return shlex.quote(command)
+ return "'{}'".format(command.replace("'", "'\"'\"'")) # type: ignore
+
+
+def cmd_error(rc, o, e):
+ s = "rc {}".format(rc)
+ o = "\n\tstdout: " + o.strip() if o and o.strip() else ""
+ e = "\n\tstderr: " + e.strip() if e and e.strip() else ""
+ return s + o + e
+
+
+def proc_error(p, o, e):
+ args = p.args if is_string(p.args) else " ".join(p.args)
+ s = "rc {} pid {}\n\targs: {}".format(p.returncode, p.pid, args)
+ o = "\n\tstdout: " + o.strip() if o and o.strip() else ""
+ e = "\n\tstderr: " + e.strip() if e and e.strip() else ""
+ return s + o + e
+
+
+def comm_error(p):
+ rc = p.poll()
+ assert rc is not None
+ if not hasattr(p, "saved_output"):
+ p.saved_output = p.communicate()
+ return proc_error(p, *p.saved_output)
+
+
+class Commander(object): # pylint: disable=R0205
+ """
+ Commander.
+
+ An object that can execute commands.
+ """
+
+ tmux_wait_gen = 0
+
+ def __init__(self, name, logger=None):
+ """Create a Commander."""
+ self.name = name
+ self.last = None
+ self.exec_paths = {}
+ self.pre_cmd = []
+ self.pre_cmd_str = ""
+
+ if not logger:
+ self.logger = logging.getLogger(__name__ + ".commander." + name)
+ else:
+ self.logger = logger
+
+ self.cwd = self.cmd_raises("pwd").strip()
+
+ def set_logger(self, logfile):
+ self.logger = logging.getLogger(__name__ + ".commander." + self.name)
+ if is_string(logfile):
+ handler = logging.FileHandler(logfile, mode="w")
+ else:
+ handler = logging.StreamHandler(logfile)
+
+ fmtstr = "%(asctime)s.%(msecs)03d %(levelname)s: {}({}): %(message)s".format(
+ self.__class__.__name__, self.name
+ )
+ handler.setFormatter(logging.Formatter(fmt=fmtstr))
+ self.logger.addHandler(handler)
+
+ def set_pre_cmd(self, pre_cmd=None):
+ if not pre_cmd:
+ self.pre_cmd = []
+ self.pre_cmd_str = ""
+ else:
+ self.pre_cmd = pre_cmd
+ self.pre_cmd_str = " ".join(self.pre_cmd) + " "
+
+ def __str__(self):
+ return "Commander({})".format(self.name)
+
+ def get_exec_path(self, binary):
+ """Return the full path to the binary executable.
+
+ `binary` :: binary name or list of binary names
+ """
+ if is_string(binary):
+ bins = [binary]
+ else:
+ bins = binary
+ for b in bins:
+ if b in self.exec_paths:
+ return self.exec_paths[b]
+
+ rc, output, _ = self.cmd_status("which " + b, warn=False)
+ if not rc:
+ return os.path.abspath(output.strip())
+ return None
+
+ def get_tmp_dir(self, uniq):
+ return os.path.join(tempfile.mkdtemp(), uniq)
+
+ def test(self, flags, arg):
+ """Run test binary, with flags and arg"""
+ test_path = self.get_exec_path(["test"])
+ rc, output, _ = self.cmd_status([test_path, flags, arg], warn=False)
+ return not rc
+
+ def path_exists(self, path):
+ """Check if path exists."""
+ return self.test("-e", path)
+
+ def _get_cmd_str(self, cmd):
+ if is_string(cmd):
+ return self.pre_cmd_str + cmd
+ cmd = self.pre_cmd + cmd
+ return " ".join(cmd)
+
+ def _get_sub_args(self, cmd, defaults, **kwargs):
+ if is_string(cmd):
+ defaults["shell"] = True
+ pre_cmd = self.pre_cmd_str
+ else:
+ defaults["shell"] = False
+ pre_cmd = self.pre_cmd
+ cmd = [str(x) for x in cmd]
+ defaults.update(kwargs)
+ return pre_cmd, cmd, defaults
+
+ def _popen(self, method, cmd, skip_pre_cmd=False, **kwargs):
+ if sys.version_info[0] >= 3:
+ defaults = {
+ "encoding": "utf-8",
+ "stdout": subprocess.PIPE,
+ "stderr": subprocess.PIPE,
+ }
+ else:
+ defaults = {
+ "stdout": subprocess.PIPE,
+ "stderr": subprocess.PIPE,
+ }
+ pre_cmd, cmd, defaults = self._get_sub_args(cmd, defaults, **kwargs)
+
+ self.logger.debug('%s: %s("%s", kwargs: %s)', self, method, cmd, defaults)
+
+ actual_cmd = cmd if skip_pre_cmd else pre_cmd + cmd
+ p = subprocess.Popen(actual_cmd, **defaults)
+ if not hasattr(p, "args"):
+ p.args = actual_cmd
+ return p, actual_cmd
+
+ def set_cwd(self, cwd):
+ self.logger.warning("%s: 'cd' (%s) does not work outside namespaces", self, cwd)
+ self.cwd = cwd
+
+ def popen(self, cmd, **kwargs):
+ """
+ Creates a pipe with the given `command`.
+
+ Args:
+ command: `str` or `list` of command to open a pipe with.
+ **kwargs: kwargs is eventually passed on to Popen. If `command` is a string
+ then will be invoked with shell=True, otherwise `command` is a list and
+ will be invoked with shell=False.
+
+ Returns:
+ a subprocess.Popen object.
+ """
+ p, _ = self._popen("popen", cmd, **kwargs)
+ return p
+
+ def cmd_status(self, cmd, raises=False, warn=True, stdin=None, **kwargs):
+ """Execute a command."""
+
+ # We are not a shell like mininet, so we need to intercept this
+ chdir = False
+ if not is_string(cmd):
+ cmds = cmd
+ else:
+ # XXX we can drop this when the code stops assuming it works
+ m = re.match(r"cd(\s*|\s+(\S+))$", cmd)
+ if m and m.group(2):
+ self.logger.warning(
+ "Bad call to 'cd' (chdir) emulating, use self.set_cwd():\n%s",
+ "".join(traceback.format_stack(limit=12)),
+ )
+ assert is_string(cmd)
+ chdir = True
+ cmd += " && pwd"
+
+ # If we are going to run under bash then we don't need shell=True!
+ cmds = ["/bin/bash", "-c", cmd]
+
+ pinput = None
+
+ if is_string(stdin) or isinstance(stdin, bytes):
+ pinput = stdin
+ stdin = subprocess.PIPE
+
+ p, actual_cmd = self._popen("cmd_status", cmds, stdin=stdin, **kwargs)
+ stdout, stderr = p.communicate(input=pinput)
+ rc = p.wait()
+
+ # For debugging purposes.
+ self.last = (rc, actual_cmd, cmd, stdout, stderr)
+
+ if rc:
+ if warn:
+ self.logger.warning(
+ "%s: proc failed: %s:", self, proc_error(p, stdout, stderr)
+ )
+ if raises:
+ # error = Exception("stderr: {}".format(stderr))
+ # This annoyingly doesn't' show stderr when printed normally
+ error = subprocess.CalledProcessError(rc, actual_cmd)
+ error.stdout, error.stderr = stdout, stderr
+ raise error
+ elif chdir:
+ self.set_cwd(stdout.strip())
+
+ return rc, stdout, stderr
+
+ def cmd_legacy(self, cmd, **kwargs):
+ """Execute a command with stdout and stderr joined, *IGNORES ERROR*."""
+
+ defaults = {"stderr": subprocess.STDOUT}
+ defaults.update(kwargs)
+ _, stdout, _ = self.cmd_status(cmd, raises=False, **defaults)
+ return stdout
+
+ def cmd_raises(self, cmd, **kwargs):
+ """Execute a command. Raise an exception on errors"""
+
+ rc, stdout, _ = self.cmd_status(cmd, raises=True, **kwargs)
+ assert rc == 0
+ return stdout
+
+ # Run a command in a new window (gnome-terminal, screen, tmux, xterm)
+ def run_in_window(
+ self,
+ cmd,
+ wait_for=False,
+ background=False,
+ name=None,
+ title=None,
+ forcex=False,
+ new_window=False,
+ tmux_target=None,
+ ):
+ """
+ Run a command in a new window (TMUX, Screen or XTerm).
+
+ Args:
+ wait_for: True to wait for exit from command or `str` as channel neme to signal on exit, otherwise False
+ background: Do not change focus to new window.
+ title: Title for new pane (tmux) or window (xterm).
+ name: Name of the new window (tmux)
+ forcex: Force use of X11.
+ new_window: Open new window (instead of pane) in TMUX
+ tmux_target: Target for tmux pane.
+
+ Returns:
+ the pane/window identifier from TMUX (depends on `new_window`)
+ """
+
+ channel = None
+ if is_string(wait_for):
+ channel = wait_for
+ elif wait_for is True:
+ channel = "{}-wait-{}".format(os.getpid(), Commander.tmux_wait_gen)
+ Commander.tmux_wait_gen += 1
+
+ sudo_path = self.get_exec_path(["sudo"])
+ nscmd = sudo_path + " " + self.pre_cmd_str + cmd
+ if "TMUX" in os.environ and not forcex:
+ cmd = [self.get_exec_path("tmux")]
+ if new_window:
+ cmd.append("new-window")
+ cmd.append("-P")
+ if name:
+ cmd.append("-n")
+ cmd.append(name)
+ if tmux_target:
+ cmd.append("-t")
+ cmd.append(tmux_target)
+ else:
+ cmd.append("split-window")
+ cmd.append("-P")
+ cmd.append("-h")
+ if not tmux_target:
+ tmux_target = os.getenv("TMUX_PANE", "")
+ if background:
+ cmd.append("-d")
+ if tmux_target:
+ cmd.append("-t")
+ cmd.append(tmux_target)
+ if title:
+ nscmd = "printf '\033]2;{}\033\\'; {}".format(title, nscmd)
+ if channel:
+ nscmd = 'trap "tmux wait -S {}; exit 0" EXIT; {}'.format(channel, nscmd)
+ cmd.append(nscmd)
+ elif "STY" in os.environ and not forcex:
+ # wait for not supported in screen for now
+ channel = None
+ cmd = [self.get_exec_path("screen")]
+ if title:
+ cmd.append("-t")
+ cmd.append(title)
+ if not os.path.exists(
+ "/run/screen/S-{}/{}".format(os.environ["USER"], os.environ["STY"])
+ ):
+ cmd = ["sudo", "-u", os.environ["SUDO_USER"]] + cmd
+ cmd.extend(nscmd.split(" "))
+ elif "DISPLAY" in os.environ:
+ # We need it broken up for xterm
+ user_cmd = cmd
+ cmd = [self.get_exec_path("xterm")]
+ if "SUDO_USER" in os.environ:
+ cmd = [self.get_exec_path("sudo"), "-u", os.environ["SUDO_USER"]] + cmd
+ if title:
+ cmd.append("-T")
+ cmd.append(title)
+ cmd.append("-e")
+ cmd.append(sudo_path)
+ cmd.extend(self.pre_cmd)
+ cmd.extend(["bash", "-c", user_cmd])
+ # if channel:
+ # return self.cmd_raises(cmd, skip_pre_cmd=True)
+ # else:
+ p = self.popen(
+ cmd,
+ skip_pre_cmd=True,
+ stdin=None,
+ shell=False,
+ )
+ time_mod.sleep(2)
+ if p.poll() is not None:
+ self.logger.error("%s: Failed to launch xterm: %s", self, comm_error(p))
+ return p
+ else:
+ self.logger.error(
+ "DISPLAY, STY, and TMUX not in environment, can't open window"
+ )
+ raise Exception("Window requestd but TMUX, Screen and X11 not available")
+
+ pane_info = self.cmd_raises(cmd, skip_pre_cmd=True).strip()
+
+ # Re-adjust the layout
+ if "TMUX" in os.environ:
+ self.cmd_status(
+ "tmux select-layout -t {} tiled".format(
+ pane_info if not tmux_target else tmux_target
+ ),
+ skip_pre_cmd=True,
+ )
+
+ # Wait here if we weren't handed the channel to wait for
+ if channel and wait_for is True:
+ cmd = [self.get_exec_path("tmux"), "wait", channel]
+ self.cmd_status(cmd, skip_pre_cmd=True)
+
+ return pane_info
+
+ def delete(self):
+ pass
+
+
+class LinuxNamespace(Commander):
+ """
+ A linux Namespace.
+
+ An object that creates and executes commands in a linux namespace
+ """
+
+ def __init__(
+ self,
+ name,
+ net=True,
+ mount=True,
+ uts=True,
+ cgroup=False,
+ ipc=False,
+ pid=False,
+ time=False,
+ user=False,
+ set_hostname=True,
+ private_mounts=None,
+ logger=None,
+ ):
+ """
+ Create a new linux namespace.
+
+ Args:
+ name: Internal name for the namespace.
+ net: Create network namespace.
+ mount: Create network namespace.
+ uts: Create UTS (hostname) namespace.
+ cgroup: Create cgroup namespace.
+ ipc: Create IPC namespace.
+ pid: Create PID namespace, also mounts new /proc.
+ time: Create time namespace.
+ user: Create user namespace, also keeps capabilities.
+ set_hostname: Set the hostname to `name`, uts must also be True.
+ private_mounts: List of strings of the form
+ "[/external/path:]/internal/path. If no external path is specified a
+ tmpfs is mounted on the internal path. Any paths specified are first
+ passed to `mkdir -p`.
+ logger: Passed to superclass.
+ """
+ super(LinuxNamespace, self).__init__(name, logger)
+
+ self.logger.debug("%s: Creating", self)
+
+ self.intfs = []
+
+ nslist = []
+ cmd = ["/usr/bin/unshare"]
+ flags = "-"
+ self.ifnetns = {}
+
+ if cgroup:
+ nslist.append("cgroup")
+ flags += "C"
+ if ipc:
+ nslist.append("ipc")
+ flags += "i"
+ if mount:
+ nslist.append("mnt")
+ flags += "m"
+ if net:
+ nslist.append("net")
+ flags += "n"
+ if pid:
+ nslist.append("pid")
+ flags += "p"
+ cmd.append("--mount-proc")
+ if time:
+ # XXX this filename is probably wrong
+ nslist.append("time")
+ flags += "T"
+ if user:
+ nslist.append("user")
+ flags += "U"
+ cmd.append("--keep-caps")
+ if uts:
+ nslist.append("uts")
+ cmd.append("--uts")
+
+ cmd.append(flags)
+ cmd.append("/bin/cat")
+
+ # Using cat and a stdin PIPE is nice as it will exit when we do. However, we
+ # also detach it from the pgid so that signals do not propagate to it. This is
+ # b/c it would exit early (e.g., ^C) then, at least the main micronet proc which
+ # has no other processes like frr daemons running, will take the main network
+ # namespace with it, which will remove the bridges and the veth pair (because
+ # the bridge side veth is deleted).
+ self.logger.debug("%s: creating namespace process: %s", self, cmd)
+ p = subprocess.Popen(
+ cmd,
+ stdin=subprocess.PIPE,
+ stdout=open("/dev/null", "w"),
+ stderr=open("/dev/null", "w"),
+ preexec_fn=os.setsid, # detach from pgid so signals don't propogate
+ shell=False,
+ )
+ self.p = p
+ self.pid = p.pid
+
+ self.logger.debug("%s: namespace pid: %d", self, self.pid)
+
+ # -----------------------------------------------
+ # Now let's wait until unshare completes it's job
+ # -----------------------------------------------
+ timeout = Timeout(30)
+ while p.poll() is None and not timeout.is_expired():
+ for fname in tuple(nslist):
+ ours = os.readlink("/proc/self/ns/{}".format(fname))
+ theirs = os.readlink("/proc/{}/ns/{}".format(self.pid, fname))
+ # See if their namespace is different
+ if ours != theirs:
+ nslist.remove(fname)
+ if not nslist:
+ break
+ elapsed = int(timeout.elapsed())
+ if elapsed <= 3:
+ time_mod.sleep(0.1)
+ elif elapsed > 10:
+ self.logger.warning("%s: unshare taking more than %ss", self, elapsed)
+ time_mod.sleep(3)
+ else:
+ self.logger.info("%s: unshare taking more than %ss", self, elapsed)
+ time_mod.sleep(1)
+ assert p.poll() is None, "unshare unexpectedly exited!"
+ assert not nslist, "unshare never unshared!"
+
+ # Set pre-command based on our namespace proc
+ self.base_pre_cmd = ["/usr/bin/nsenter", "-a", "-t", str(self.pid)]
+ if not pid:
+ self.base_pre_cmd.append("-F")
+ self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + self.cwd])
+
+ # Remount sysfs and cgroup to pickup any changes
+ self.cmd_raises("mount -t sysfs sysfs /sys")
+ self.cmd_raises(
+ "mount -o rw,nosuid,nodev,noexec,relatime -t cgroup2 cgroup /sys/fs/cgroup"
+ )
+
+ # Set the hostname to the namespace name
+ if uts and set_hostname:
+ # Debugging get the root hostname
+ self.cmd_raises("hostname " + self.name)
+ nroot = subprocess.check_output("hostname")
+ if root_hostname != nroot:
+ result = self.p.poll()
+ assert root_hostname == nroot, "STATE of namespace process {}".format(
+ result
+ )
+
+ if private_mounts:
+ if is_string(private_mounts):
+ private_mounts = [private_mounts]
+ for m in private_mounts:
+ s = m.split(":", 1)
+ if len(s) == 1:
+ self.tmpfs_mount(s[0])
+ else:
+ self.bind_mount(s[0], s[1])
+
+ o = self.cmd_legacy("ls -l /proc/{}/ns".format(self.pid))
+ self.logger.debug("namespaces:\n %s", o)
+
+ # Doing this here messes up all_protocols ipv6 check
+ self.cmd_raises("ip link set lo up")
+
+ def __str__(self):
+ return "LinuxNamespace({})".format(self.name)
+
+ def tmpfs_mount(self, inner):
+ self.cmd_raises("mkdir -p " + inner)
+ self.cmd_raises("mount -n -t tmpfs tmpfs " + inner)
+
+ def bind_mount(self, outer, inner):
+ self.cmd_raises("mkdir -p " + inner)
+ self.cmd_raises("mount --rbind {} {} ".format(outer, inner))
+
+ def add_vlan(self, vlanname, linkiface, vlanid):
+ self.logger.debug("Adding VLAN interface: %s (%s)", vlanname, vlanid)
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ self.cmd_raises(
+ [
+ ip_path,
+ "link",
+ "add",
+ "link",
+ linkiface,
+ "name",
+ vlanname,
+ "type",
+ "vlan",
+ "id",
+ vlanid,
+ ]
+ )
+ self.cmd_raises([ip_path, "link", "set", "dev", vlanname, "up"])
+
+ def add_loop(self, loopname):
+ self.logger.debug("Adding Linux iface: %s", loopname)
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ self.cmd_raises([ip_path, "link", "add", loopname, "type", "dummy"])
+ self.cmd_raises([ip_path, "link", "set", "dev", loopname, "up"])
+
+ def add_l3vrf(self, vrfname, tableid):
+ self.logger.debug("Adding Linux VRF: %s", vrfname)
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ self.cmd_raises(
+ [ip_path, "link", "add", vrfname, "type", "vrf", "table", tableid]
+ )
+ self.cmd_raises([ip_path, "link", "set", "dev", vrfname, "up"])
+
+ def del_iface(self, iface):
+ self.logger.debug("Removing Linux Iface: %s", iface)
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ self.cmd_raises([ip_path, "link", "del", iface])
+
+ def attach_iface_to_l3vrf(self, ifacename, vrfname):
+ self.logger.debug("Attaching Iface %s to Linux VRF %s", ifacename, vrfname)
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ if vrfname:
+ self.cmd_raises(
+ [ip_path, "link", "set", "dev", ifacename, "master", vrfname]
+ )
+ else:
+ self.cmd_raises([ip_path, "link", "set", "dev", ifacename, "nomaster"])
+
+ def add_netns(self, ns):
+ self.logger.debug("Adding network namespace %s", ns)
+
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ if os.path.exists("/run/netns/{}".format(ns)):
+ self.logger.warning("%s: Removing existing nsspace %s", self, ns)
+ try:
+ self.delete_netns(ns)
+ except Exception as ex:
+ self.logger.warning(
+ "%s: Couldn't remove existing nsspace %s: %s",
+ self,
+ ns,
+ str(ex),
+ exc_info=True,
+ )
+ self.cmd_raises([ip_path, "netns", "add", ns])
+
+ def delete_netns(self, ns):
+ self.logger.debug("Deleting network namespace %s", ns)
+
+ ip_path = self.get_exec_path("ip")
+ assert ip_path, "XXX missing ip command!"
+ self.cmd_raises([ip_path, "netns", "delete", ns])
+
+ def set_intf_netns(self, intf, ns, up=False):
+ # In case a user hard-codes 1 thinking it "resets"
+ ns = str(ns)
+ if ns == "1":
+ ns = str(self.pid)
+
+ self.logger.debug("Moving interface %s to namespace %s", intf, ns)
+
+ cmd = "ip link set {} netns " + ns
+ if up:
+ cmd += " up"
+ self.intf_ip_cmd(intf, cmd)
+ if ns == str(self.pid):
+ # If we are returning then remove from dict
+ if intf in self.ifnetns:
+ del self.ifnetns[intf]
+ else:
+ self.ifnetns[intf] = ns
+
+ def reset_intf_netns(self, intf):
+ self.logger.debug("Moving interface %s to default namespace", intf)
+ self.set_intf_netns(intf, str(self.pid))
+
+ def intf_ip_cmd(self, intf, cmd):
+ """Run an ip command for considering an interfaces possible namespace.
+
+ `cmd` - format is run using the interface name on the command
+ """
+ if intf in self.ifnetns:
+ assert cmd.startswith("ip ")
+ cmd = "ip -n " + self.ifnetns[intf] + cmd[2:]
+ self.cmd_raises(cmd.format(intf))
+
+ def set_cwd(self, cwd):
+ # Set pre-command based on our namespace proc
+ self.logger.debug("%s: new CWD %s", self, cwd)
+ self.set_pre_cmd(self.base_pre_cmd + ["--wd=" + cwd])
+
+ def register_interface(self, ifname):
+ if ifname not in self.intfs:
+ self.intfs.append(ifname)
+
+ def delete(self):
+ if self.p and self.p.poll() is None:
+ if sys.version_info[0] >= 3:
+ try:
+ self.p.terminate()
+ self.p.communicate(timeout=10)
+ except subprocess.TimeoutExpired:
+ self.p.kill()
+ self.p.communicate(timeout=2)
+ else:
+ self.p.kill()
+ self.p.communicate()
+ self.set_pre_cmd(["/bin/false"])
+
+
+class SharedNamespace(Commander):
+ """
+ Share another namespace.
+
+ An object that executes commands in an existing pid's linux namespace
+ """
+
+ def __init__(self, name, pid, logger=None):
+ """
+ Share a linux namespace.
+
+ Args:
+ name: Internal name for the namespace.
+ pid: PID of the process to share with.
+ """
+ super(SharedNamespace, self).__init__(name, logger)
+
+ self.logger.debug("%s: Creating", self)
+
+ self.pid = pid
+ self.intfs = []
+
+ # Set pre-command based on our namespace proc
+ self.set_pre_cmd(
+ ["/usr/bin/nsenter", "-a", "-t", str(self.pid), "--wd=" + self.cwd]
+ )
+
+ def __str__(self):
+ return "SharedNamespace({})".format(self.name)
+
+ def set_cwd(self, cwd):
+ # Set pre-command based on our namespace proc
+ self.logger.debug("%s: new CWD %s", self, cwd)
+ self.set_pre_cmd(["/usr/bin/nsenter", "-a", "-t", str(self.pid), "--wd=" + cwd])
+
+ def register_interface(self, ifname):
+ if ifname not in self.intfs:
+ self.intfs.append(ifname)
+
+
+class Bridge(SharedNamespace):
+ """
+ A linux bridge.
+ """
+
+ next_brid_ord = 0
+
+ @classmethod
+ def _get_next_brid(cls):
+ brid_ord = cls.next_brid_ord
+ cls.next_brid_ord += 1
+ return brid_ord
+
+ def __init__(self, name=None, unet=None, logger=None):
+ """Create a linux Bridge."""
+
+ self.unet = unet
+ self.brid_ord = self._get_next_brid()
+ if name:
+ self.brid = name
+ else:
+ self.brid = "br{}".format(self.brid_ord)
+ name = self.brid
+
+ super(Bridge, self).__init__(name, unet.pid, logger)
+
+ self.logger.debug("Bridge: Creating")
+
+ assert len(self.brid) <= 16 # Make sure fits in IFNAMSIZE
+ self.cmd_raises("ip link delete {} || true".format(self.brid))
+ self.cmd_raises("ip link add {} type bridge".format(self.brid))
+ self.cmd_raises("ip link set {} up".format(self.brid))
+
+ self.logger.debug("%s: Created, Running", self)
+
+ def __str__(self):
+ return "Bridge({})".format(self.brid)
+
+ def delete(self):
+ """Stop the bridge (i.e., delete the linux resources)."""
+
+ rc, o, e = self.cmd_status("ip link show {}".format(self.brid), warn=False)
+ if not rc:
+ rc, o, e = self.cmd_status(
+ "ip link delete {}".format(self.brid), warn=False
+ )
+ if rc:
+ self.logger.error(
+ "%s: error deleting bridge %s: %s",
+ self,
+ self.brid,
+ cmd_error(rc, o, e),
+ )
+ else:
+ self.logger.debug("%s: Deleted.", self)
+
+
+class Micronet(LinuxNamespace): # pylint: disable=R0205
+ """
+ Micronet.
+ """
+
+ def __init__(self):
+ """Create a Micronet."""
+
+ self.hosts = {}
+ self.switches = {}
+ self.links = {}
+ self.macs = {}
+ self.rmacs = {}
+
+ super(Micronet, self).__init__("micronet", mount=True, net=True, uts=True)
+
+ self.logger.debug("%s: Creating", self)
+
+ def __str__(self):
+ return "Micronet()"
+
+ def __getitem__(self, key):
+ if key in self.switches:
+ return self.switches[key]
+ return self.hosts[key]
+
+ def add_host(self, name, cls=LinuxNamespace, **kwargs):
+ """Add a host to micronet."""
+
+ self.logger.debug("%s: add_host %s", self, name)
+
+ self.hosts[name] = cls(name, **kwargs)
+ # Create a new mounted FS for tracking nested network namespaces creatd by the
+ # user with `ip netns add`
+ self.hosts[name].tmpfs_mount("/run/netns")
+
+ def add_link(self, name1, name2, if1, if2):
+ """Add a link between switch and host to micronet."""
+ isp2p = False
+ if name1 in self.switches:
+ assert name2 in self.hosts
+ elif name2 in self.switches:
+ assert name1 in self.hosts
+ name1, name2 = name2, name1
+ if1, if2 = if2, if1
+ else:
+ # p2p link
+ assert name1 in self.hosts
+ assert name2 in self.hosts
+ isp2p = True
+
+ lname = "{}:{}-{}:{}".format(name1, if1, name2, if2)
+ self.logger.debug("%s: add_link %s%s", self, lname, " p2p" if isp2p else "")
+ self.links[lname] = (name1, if1, name2, if2)
+
+ # And create the veth now.
+ if isp2p:
+ lhost, rhost = self.hosts[name1], self.hosts[name2]
+ lifname = "i1{:x}".format(lhost.pid)
+ rifname = "i2{:x}".format(rhost.pid)
+ self.cmd_raises(
+ "ip link add {} type veth peer name {}".format(lifname, rifname)
+ )
+
+ self.cmd_raises("ip link set {} netns {}".format(lifname, lhost.pid))
+ lhost.cmd_raises("ip link set {} name {}".format(lifname, if1))
+ lhost.cmd_raises("ip link set {} up".format(if1))
+ lhost.register_interface(if1)
+
+ self.cmd_raises("ip link set {} netns {}".format(rifname, rhost.pid))
+ rhost.cmd_raises("ip link set {} name {}".format(rifname, if2))
+ rhost.cmd_raises("ip link set {} up".format(if2))
+ rhost.register_interface(if2)
+ else:
+ switch = self.switches[name1]
+ host = self.hosts[name2]
+
+ assert len(if1) <= 16 and len(if2) <= 16 # Make sure fits in IFNAMSIZE
+
+ self.logger.debug("%s: Creating veth pair for link %s", self, lname)
+ self.cmd_raises(
+ "ip link add {} type veth peer name {} netns {}".format(
+ if1, if2, host.pid
+ )
+ )
+ self.cmd_raises("ip link set {} netns {}".format(if1, switch.pid))
+ switch.register_interface(if1)
+ host.register_interface(if2)
+ self.cmd_raises("ip link set {} master {}".format(if1, switch.brid))
+ self.cmd_raises("ip link set {} up".format(if1))
+ host.cmd_raises("ip link set {} up".format(if2))
+
+ # Cache the MAC values, and reverse mapping
+ self.get_mac(name1, if1)
+ self.get_mac(name2, if2)
+
+ def add_switch(self, name):
+ """Add a switch to micronet."""
+
+ self.logger.debug("%s: add_switch %s", self, name)
+ self.switches[name] = Bridge(name, self)
+
+ def get_mac(self, name, ifname):
+ if name in self.hosts:
+ dev = self.hosts[name]
+ else:
+ dev = self.switches[name]
+
+ if (name, ifname) not in self.macs:
+ _, output, _ = dev.cmd_status("ip -o link show " + ifname)
+ m = re.match(".*link/(loopback|ether) ([0-9a-fA-F:]+) .*", output)
+ mac = m.group(2)
+ self.macs[(name, ifname)] = mac
+ self.rmacs[mac] = (name, ifname)
+
+ return self.macs[(name, ifname)]
+
+ def delete(self):
+ """Delete the micronet topology."""
+
+ self.logger.debug("%s: Deleting.", self)
+
+ for lname, (_, _, rname, rif) in self.links.items():
+ host = self.hosts[rname]
+
+ self.logger.debug("%s: Deleting veth pair for link %s", self, lname)
+
+ rc, o, e = host.cmd_status("ip link delete {}".format(rif), warn=False)
+ if rc:
+ self.logger.error(
+ "Error deleting veth pair %s: %s", lname, cmd_error(rc, o, e)
+ )
+
+ self.links = {}
+
+ for host in self.hosts.values():
+ try:
+ host.delete()
+ except Exception as error:
+ self.logger.error(
+ "%s: error while deleting host %s: %s", self, host, error
+ )
+
+ self.hosts = {}
+
+ for switch in self.switches.values():
+ try:
+ switch.delete()
+ except Exception as error:
+ self.logger.error(
+ "%s: error while deleting switch %s: %s", self, switch, error
+ )
+ self.switches = {}
+
+ self.logger.debug("%s: Deleted.", self)
+
+ super(Micronet, self).delete()
+
+
+# ---------------------------
+# Root level utility function
+# ---------------------------
+
+
+def get_exec_path(binary):
+ base = Commander("base")
+ return base.get_exec_path(binary)
+
+
+commander = Commander("micronet")
diff --git a/tests/topotests/lib/micronet_cli.py b/tests/topotests/lib/micronet_cli.py
new file mode 100644
index 0000000..ef804f6
--- /dev/null
+++ b/tests/topotests/lib/micronet_cli.py
@@ -0,0 +1,319 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 24 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+import argparse
+import logging
+import os
+import pty
+import re
+import readline
+import select
+import socket
+import subprocess
+import sys
+import tempfile
+import termios
+import tty
+
+
+ENDMARKER = b"\x00END\x00"
+
+
+def lineiter(sock):
+ s = ""
+ while True:
+ sb = sock.recv(256)
+ if not sb:
+ return
+
+ s += sb.decode("utf-8")
+ i = s.find("\n")
+ if i != -1:
+ yield s[:i]
+ s = s[i + 1 :]
+
+
+def spawn(unet, host, cmd):
+ if sys.stdin.isatty():
+ old_tty = termios.tcgetattr(sys.stdin)
+ tty.setraw(sys.stdin.fileno())
+ try:
+ master_fd, slave_fd = pty.openpty()
+
+ # use os.setsid() make it run in a new process group, or bash job
+ # control will not be enabled
+ p = unet.hosts[host].popen(
+ cmd,
+ preexec_fn=os.setsid,
+ stdin=slave_fd,
+ stdout=slave_fd,
+ stderr=slave_fd,
+ universal_newlines=True,
+ )
+
+ while p.poll() is None:
+ r, w, e = select.select([sys.stdin, master_fd], [], [], 0.25)
+ if sys.stdin in r:
+ d = os.read(sys.stdin.fileno(), 10240)
+ os.write(master_fd, d)
+ elif master_fd in r:
+ o = os.read(master_fd, 10240)
+ if o:
+ os.write(sys.stdout.fileno(), o)
+ finally:
+ # restore tty settings back
+ if sys.stdin.isatty():
+ termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
+
+
+def doline(unet, line, writef):
+ def host_cmd_split(unet, cmd):
+ csplit = cmd.split()
+ for i, e in enumerate(csplit):
+ if e not in unet.hosts:
+ break
+ hosts = csplit[:i]
+ if not hosts:
+ hosts = sorted(unet.hosts.keys())
+ cmd = " ".join(csplit[i:])
+ return hosts, cmd
+
+ line = line.strip()
+ m = re.match(r"^(\S+)(?:\s+(.*))?$", line)
+ if not m:
+ return True
+
+ cmd = m.group(1)
+ oargs = m.group(2) if m.group(2) else ""
+ if cmd == "q" or cmd == "quit":
+ return False
+ if cmd == "hosts":
+ writef("%% hosts: %s\n" % " ".join(sorted(unet.hosts.keys())))
+ elif cmd in ["term", "vtysh", "xterm"]:
+ args = oargs.split()
+ if not args or (len(args) == 1 and args[0] == "*"):
+ args = sorted(unet.hosts.keys())
+ hosts = [unet.hosts[x] for x in args if x in unet.hosts]
+ for host in hosts:
+ if cmd == "t" or cmd == "term":
+ host.run_in_window("bash", title="sh-%s" % host)
+ elif cmd == "v" or cmd == "vtysh":
+ host.run_in_window("vtysh", title="vt-%s" % host)
+ elif cmd == "x" or cmd == "xterm":
+ host.run_in_window("bash", title="sh-%s" % host, forcex=True)
+ elif cmd == "sh":
+ hosts, cmd = host_cmd_split(unet, oargs)
+ for host in hosts:
+ if sys.stdin.isatty():
+ spawn(unet, host, cmd)
+ else:
+ if len(hosts) > 1:
+ writef("------ Host: %s ------\n" % host)
+ output = unet.hosts[host].cmd_legacy(cmd)
+ writef(output)
+ if len(hosts) > 1:
+ writef("------- End: %s ------\n" % host)
+ writef("\n")
+ elif cmd == "h" or cmd == "help":
+ writef(
+ """
+Commands:
+ help :: this help
+ sh [hosts] <shell-command> :: execute <shell-command> on <host>
+ term [hosts] :: open shell terminals for hosts
+ vtysh [hosts] :: open vtysh terminals for hosts
+ [hosts] <vtysh-command> :: execute vtysh-command on hosts\n\n"""
+ )
+ else:
+ hosts, cmd = host_cmd_split(unet, line)
+ for host in hosts:
+ if len(hosts) > 1:
+ writef("------ Host: %s ------\n" % host)
+ output = unet.hosts[host].cmd_legacy('vtysh -c "{}"'.format(cmd))
+ writef(output)
+ if len(hosts) > 1:
+ writef("------- End: %s ------\n" % host)
+ writef("\n")
+ return True
+
+
+def cli_server_setup(unet):
+ sockdir = tempfile.mkdtemp("-sockdir", "pyt")
+ sockpath = os.path.join(sockdir, "cli-server.sock")
+ try:
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.settimeout(10)
+ sock.bind(sockpath)
+ sock.listen(1)
+ return sock, sockdir, sockpath
+ except Exception:
+ unet.cmd_status("rm -rf " + sockdir)
+ raise
+
+
+def cli_server(unet, server_sock):
+ sock, addr = server_sock.accept()
+
+ # Go into full non-blocking mode now
+ sock.settimeout(None)
+
+ for line in lineiter(sock):
+ line = line.strip()
+
+ def writef(x):
+ xb = x.encode("utf-8")
+ sock.send(xb)
+
+ if not doline(unet, line, writef):
+ return
+ sock.send(ENDMARKER)
+
+
+def cli_client(sockpath, prompt="unet> "):
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ sock.settimeout(10)
+ sock.connect(sockpath)
+
+ # Go into full non-blocking mode now
+ sock.settimeout(None)
+
+ print("\n--- Micronet CLI Starting ---\n\n")
+ while True:
+ if sys.version_info[0] == 2:
+ line = raw_input(prompt) # pylint: disable=E0602
+ else:
+ line = input(prompt)
+ if line is None:
+ return
+
+ # Need to put \n back
+ line += "\n"
+
+ # Send the CLI command
+ sock.send(line.encode("utf-8"))
+
+ def bendswith(b, sentinel):
+ slen = len(sentinel)
+ return len(b) >= slen and b[-slen:] == sentinel
+
+ # Collect the output
+ rb = b""
+ while not bendswith(rb, ENDMARKER):
+ lb = sock.recv(4096)
+ if not lb:
+ return
+ rb += lb
+
+ # Remove the marker
+ rb = rb[: -len(ENDMARKER)]
+
+ # Write the output
+ sys.stdout.write(rb.decode("utf-8"))
+
+
+def local_cli(unet, outf, prompt="unet> "):
+ print("\n--- Micronet CLI Starting ---\n\n")
+ while True:
+ if sys.version_info[0] == 2:
+ line = raw_input(prompt) # pylint: disable=E0602
+ else:
+ line = input(prompt)
+ if line is None:
+ return
+ if not doline(unet, line, outf.write):
+ return
+
+
+def cli(
+ unet,
+ histfile=None,
+ sockpath=None,
+ force_window=False,
+ title=None,
+ prompt=None,
+ background=True,
+):
+ logger = logging.getLogger("cli-client")
+
+ if prompt is None:
+ prompt = "unet> "
+
+ if force_window or not sys.stdin.isatty():
+ # Run CLI in another window b/c we have no tty.
+ sock, sockdir, sockpath = cli_server_setup(unet)
+
+ python_path = unet.get_exec_path(["python3", "python"])
+ us = os.path.realpath(__file__)
+ cmd = "{} {}".format(python_path, us)
+ if histfile:
+ cmd += " --histfile=" + histfile
+ if title:
+ cmd += " --prompt={}".format(title)
+ cmd += " " + sockpath
+
+ try:
+ unet.run_in_window(cmd, new_window=True, title=title, background=background)
+ return cli_server(unet, sock)
+ finally:
+ unet.cmd_status("rm -rf " + sockdir)
+
+ if not unet:
+ logger.debug("client-cli using sockpath %s", sockpath)
+
+ try:
+ if histfile is None:
+ histfile = os.path.expanduser("~/.micronet-history.txt")
+ if not os.path.exists(histfile):
+ if unet:
+ unet.cmd("touch " + histfile)
+ else:
+ subprocess.run("touch " + histfile)
+ if histfile:
+ readline.read_history_file(histfile)
+ except Exception:
+ pass
+
+ try:
+ if sockpath:
+ cli_client(sockpath, prompt=prompt)
+ else:
+ local_cli(unet, sys.stdout, prompt=prompt)
+ except EOFError:
+ pass
+ except Exception as ex:
+ logger.critical("cli: got exception: %s", ex, exc_info=True)
+ raise
+ finally:
+ readline.write_history_file(histfile)
+
+
+if __name__ == "__main__":
+ logging.basicConfig(level=logging.DEBUG, filename="/tmp/topotests/cli-client.log")
+ logger = logging.getLogger("cli-client")
+ logger.info("Start logging cli-client")
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--histfile", help="file to user for history")
+ parser.add_argument("--prompt-text", help="prompt string to use")
+ parser.add_argument("socket", help="path to pair of sockets to communicate over")
+ args = parser.parse_args()
+
+ prompt = "{}> ".format(args.prompt_text) if args.prompt_text else "unet> "
+ cli(None, args.histfile, args.socket, prompt=prompt)
diff --git a/tests/topotests/lib/micronet_compat.py b/tests/topotests/lib/micronet_compat.py
new file mode 100644
index 0000000..a3d3f4c
--- /dev/null
+++ b/tests/topotests/lib/micronet_compat.py
@@ -0,0 +1,266 @@
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 11 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+import glob
+import logging
+import os
+import signal
+import time
+
+from lib.micronet import LinuxNamespace, Micronet
+from lib.micronet_cli import cli
+
+
+def get_pids_with_env(has_var, has_val=None):
+ result = {}
+ for pidenv in glob.iglob("/proc/*/environ"):
+ pid = pidenv.split("/")[2]
+ try:
+ with open(pidenv, "rb") as rfb:
+ envlist = [
+ x.decode("utf-8").split("=", 1) for x in rfb.read().split(b"\0")
+ ]
+ envlist = [[x[0], ""] if len(x) == 1 else x for x in envlist]
+ envdict = dict(envlist)
+ if has_var not in envdict:
+ continue
+ if has_val is None:
+ result[pid] = envdict
+ elif envdict[has_var] == str(has_val):
+ result[pid] = envdict
+ except Exception:
+ # E.g., process exited and files are gone
+ pass
+ return result
+
+
+def _kill_piddict(pids_by_upid, sig):
+ for upid, pids in pids_by_upid:
+ logging.info(
+ "Sending %s to (%s) of micronet pid %s", sig, ", ".join(pids), upid
+ )
+ for pid in pids:
+ try:
+ os.kill(int(pid), sig)
+ except Exception:
+ pass
+
+
+def _get_our_pids():
+ ourpid = str(os.getpid())
+ piddict = get_pids_with_env("MICRONET_PID", ourpid)
+ pids = [x for x in piddict if x != ourpid]
+ if pids:
+ return {ourpid: pids}
+ return {}
+
+
+def _get_other_pids():
+ piddict = get_pids_with_env("MICRONET_PID")
+ unet_pids = {d["MICRONET_PID"] for d in piddict.values()}
+ pids_by_upid = {p: set() for p in unet_pids}
+ for pid, envdict in piddict.items():
+ pids_by_upid[envdict["MICRONET_PID"]].add(pid)
+ # Filter out any child pid sets whos micronet pid is still running
+ return {x: y for x, y in pids_by_upid.items() if x not in y}
+
+
+def _get_pids_by_upid(ours):
+ if ours:
+ return _get_our_pids()
+ return _get_other_pids()
+
+
+def _cleanup_pids(ours):
+ pids_by_upid = _get_pids_by_upid(ours).items()
+ if not pids_by_upid:
+ return
+
+ _kill_piddict(pids_by_upid, signal.SIGTERM)
+
+ # Give them 5 second to exit cleanly
+ logging.info("Waiting up to 5s to allow for clean exit of abandon'd pids")
+ for _ in range(0, 5):
+ pids_by_upid = _get_pids_by_upid(ours).items()
+ if not pids_by_upid:
+ return
+ time.sleep(1)
+
+ pids_by_upid = _get_pids_by_upid(ours).items()
+ _kill_piddict(pids_by_upid, signal.SIGKILL)
+
+
+def cleanup_current():
+ """Attempt to cleanup preview runs.
+
+ Currently this only scans for old processes.
+ """
+ logging.info("reaping current micronet processes")
+ _cleanup_pids(True)
+
+
+def cleanup_previous():
+ """Attempt to cleanup preview runs.
+
+ Currently this only scans for old processes.
+ """
+ logging.info("reaping past micronet processes")
+ _cleanup_pids(False)
+
+
+class Node(LinuxNamespace):
+ """Node (mininet compat)."""
+
+ def __init__(self, name, **kwargs):
+ """
+ Create a Node.
+ """
+ self.params = kwargs
+
+ if "private_mounts" in kwargs:
+ private_mounts = kwargs["private_mounts"]
+ else:
+ private_mounts = kwargs.get("privateDirs", [])
+
+ logger = kwargs.get("logger")
+
+ super(Node, self).__init__(name, logger=logger, private_mounts=private_mounts)
+
+ def cmd(self, cmd, **kwargs):
+ """Execute a command, joins stdout, stderr, ignores exit status."""
+
+ return super(Node, self).cmd_legacy(cmd, **kwargs)
+
+ def config(self, lo="up", **params):
+ """Called by Micronet when topology is built (but not started)."""
+ # mininet brings up loopback here.
+ del params
+ del lo
+
+ def intfNames(self):
+ return self.intfs
+
+ def terminate(self):
+ return
+
+
+class Topo(object): # pylint: disable=R0205
+ def __init__(self, *args, **kwargs):
+ raise Exception("Remove Me")
+
+
+class Mininet(Micronet):
+ """
+ Mininet using Micronet.
+ """
+
+ g_mnet_inst = None
+
+ def __init__(self, controller=None):
+ """
+ Create a Micronet.
+ """
+ assert not controller
+
+ if Mininet.g_mnet_inst is not None:
+ Mininet.g_mnet_inst.stop()
+ Mininet.g_mnet_inst = self
+
+ self.configured_hosts = set()
+ self.host_params = {}
+ self.prefix_len = 8
+
+ # SNMPd used to require this, which was set int he mininet shell
+ # that all commands executed from. This is goofy default so let's not
+ # do it if we don't have to. The snmpd.conf files have been updated
+ # to set permissions to root:frr 770 to make this unneeded in that case
+ # os.umask(0)
+
+ super(Mininet, self).__init__()
+
+ self.logger.debug("%s: Creating", self)
+
+ def __str__(self):
+ return "Mininet()"
+
+ def configure_hosts(self):
+ """
+ Configure hosts once the topology has been built.
+
+ This function can be called multiple times if routers are added to the topology
+ later.
+ """
+ if not self.hosts:
+ return
+
+ self.logger.debug("Configuring hosts: %s", self.hosts.keys())
+
+ for name in sorted(self.hosts.keys()):
+ if name in self.configured_hosts:
+ continue
+
+ host = self.hosts[name]
+ first_intf = host.intfs[0] if host.intfs else None
+ params = self.host_params[name]
+
+ if first_intf and "ip" in params:
+ ip = params["ip"]
+ i = ip.find("/")
+ if i == -1:
+ plen = self.prefix_len
+ else:
+ plen = int(ip[i + 1 :])
+ ip = ip[:i]
+
+ host.cmd_raises("ip addr add {}/{} dev {}".format(ip, plen, first_intf))
+
+ if "defaultRoute" in params:
+ host.cmd_raises(
+ "ip route add default {}".format(params["defaultRoute"])
+ )
+
+ host.config()
+
+ self.configured_hosts.add(name)
+
+ def add_host(self, name, cls=Node, **kwargs):
+ """Add a host to micronet."""
+
+ self.host_params[name] = kwargs
+ super(Mininet, self).add_host(name, cls=cls, **kwargs)
+
+ def start(self):
+ """Start the micronet topology."""
+ self.logger.debug("%s: Starting (no-op).", self)
+
+ def stop(self):
+ """Stop the mininet topology (deletes)."""
+ self.logger.debug("%s: Stopping (deleting).", self)
+
+ self.delete()
+
+ self.logger.debug("%s: Stopped (deleted).", self)
+
+ if Mininet.g_mnet_inst == self:
+ Mininet.g_mnet_inst = None
+
+ def cli(self):
+ cli(self)
diff --git a/tests/topotests/lib/ospf.py b/tests/topotests/lib/ospf.py
new file mode 100644
index 0000000..e7ea7d3
--- /dev/null
+++ b/tests/topotests/lib/ospf.py
@@ -0,0 +1,2491 @@
+#
+# 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.
+#
+
+import ipaddress
+import sys
+from copy import deepcopy
+
+# Import common_config to use commomnly used APIs
+from lib.common_config import (
+ create_common_configurations,
+ InvalidCLIError,
+ generate_ips,
+ retry,
+ run_frr_cmd,
+ validate_ip_address,
+)
+from lib.topolog import logger
+from lib.topotest import frr_unicode
+
+################################
+# Configure procs
+################################
+
+
+def create_router_ospf(tgen, topo=None, input_dict=None, build=False, load_config=True):
+ """
+ API to configure ospf on router.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ * `load_config` : Loading the config to router this is set as True.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "ospf": {
+ "router_id": "22.22.22.22",
+ "area": [{ "id": "0.0.0.0", "type": "nssa"}]
+ }
+ }
+
+ result = create_router_ospf(tgen, topo, input_dict)
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: create_router_ospf()")
+ result = False
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ topo = topo["routers"]
+ input_dict = deepcopy(input_dict)
+
+ for ospf in ["ospf", "ospf6"]:
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ if ospf not in input_dict[router]:
+ logger.debug("Router %s: %s not present in input_dict", router, ospf)
+ continue
+
+ config_data = __create_ospf_global(
+ tgen, input_dict, router, build, load_config, ospf
+ )
+ if config_data:
+ if router not in config_data_dict:
+ config_data_dict[router] = config_data
+ else:
+ config_data_dict[router].extend(config_data)
+ try:
+ result = create_common_configurations(
+ tgen, config_data_dict, ospf, build, load_config
+ )
+ except InvalidCLIError:
+ logger.error("create_router_ospf (ipv4)", exc_info=True)
+ result = False
+
+ logger.debug("Exiting lib API: create_router_ospf()")
+ return result
+
+
+def __create_ospf_global(tgen, input_dict, router, build, load_config, ospf):
+ """
+ Helper API to create ospf global configuration.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `router` : router to be configured.
+ * `build` : Only for initial setup phase this is set as True.
+ * `load_config` : Loading the config to router this is set as True.
+ * `ospf` : either 'ospf' or 'ospf6'
+
+ Usage
+ -----
+ input_dict = {
+ "routers": {
+ "r1": {
+ "links": {
+ "r3": {
+ "ipv6": "2013:13::1/64",
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r3": {
+ "area": "1.1.1.1"
+ }
+ }
+ }
+ }
+ }
+
+ Returns
+ -------
+ list of configuration commands
+ """
+
+ config_data = []
+
+ if ospf not in input_dict[router]:
+ return config_data
+
+ logger.debug("Entering lib API: __create_ospf_global()")
+
+ ospf_data = input_dict[router][ospf]
+ del_ospf_action = ospf_data.setdefault("delete", False)
+ if del_ospf_action:
+ config_data = ["no router {}".format(ospf)]
+ return config_data
+
+ cmd = "router {}".format(ospf)
+
+ config_data.append(cmd)
+
+ # router id
+ router_id = ospf_data.setdefault("router_id", None)
+ del_router_id = ospf_data.setdefault("del_router_id", False)
+ if del_router_id:
+ config_data.append("no {} router-id".format(ospf))
+ if router_id:
+ config_data.append("{} router-id {}".format(ospf, router_id))
+
+ # log-adjacency-changes
+ log_adj_changes = ospf_data.setdefault("log_adj_changes", None)
+ del_log_adj_changes = ospf_data.setdefault("del_log_adj_changes", False)
+ if del_log_adj_changes:
+ config_data.append("no log-adjacency-changes detail")
+ if log_adj_changes:
+ config_data.append("log-adjacency-changes {}".format(log_adj_changes))
+
+ # aggregation timer
+ aggr_timer = ospf_data.setdefault("aggr_timer", None)
+ del_aggr_timer = ospf_data.setdefault("del_aggr_timer", False)
+ if del_aggr_timer:
+ config_data.append("no aggregation timer")
+ if aggr_timer:
+ config_data.append("aggregation timer {}".format(aggr_timer))
+
+ # maximum path information
+ ecmp_data = ospf_data.setdefault("maximum-paths", {})
+ if ecmp_data:
+ cmd = "maximum-paths {}".format(ecmp_data)
+ del_action = ospf_data.setdefault("del_max_path", False)
+ if del_action:
+ cmd = "no maximum-paths"
+ config_data.append(cmd)
+
+ # redistribute command
+ redistribute_data = ospf_data.setdefault("redistribute", {})
+ if redistribute_data:
+ for redistribute in redistribute_data:
+ if "redist_type" not in redistribute:
+ logger.debug(
+ "Router %s: 'redist_type' not present in " "input_dict", router
+ )
+ else:
+ cmd = "redistribute {}".format(redistribute["redist_type"])
+ for red_type in redistribute_data:
+ if "route_map" in red_type:
+ cmd = cmd + " route-map {}".format(red_type["route_map"])
+ del_action = redistribute.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # area information
+ area_data = ospf_data.setdefault("area", {})
+ if area_data:
+ for area in area_data:
+ if "id" not in area:
+ logger.debug(
+ "Router %s: 'area id' not present in " "input_dict", router
+ )
+ else:
+ cmd = "area {}".format(area["id"])
+
+ if "type" in area:
+ cmd = cmd + " {}".format(area["type"])
+
+ del_action = area.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # def route information
+ def_rte_data = ospf_data.setdefault("default-information", {})
+ if def_rte_data:
+ if "originate" not in def_rte_data:
+ logger.debug(
+ "Router %s: 'originate key' not present in " "input_dict", router
+ )
+ else:
+ cmd = "default-information originate"
+
+ if "always" in def_rte_data:
+ cmd = cmd + " always"
+
+ if "metric" in def_rte_data:
+ cmd = cmd + " metric {}".format(def_rte_data["metric"])
+
+ if "metric-type" in def_rte_data:
+ cmd = cmd + " metric-type {}".format(def_rte_data["metric-type"])
+
+ if "route-map" in def_rte_data:
+ cmd = cmd + " route-map {}".format(def_rte_data["route-map"])
+
+ del_action = def_rte_data.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # summary information
+ summary_data = ospf_data.setdefault("summary-address", {})
+ if summary_data:
+ for summary in summary_data:
+ if "prefix" not in summary:
+ logger.debug(
+ "Router %s: 'summary-address' not present in " "input_dict",
+ router,
+ )
+ else:
+ cmd = "summary {}/{}".format(summary["prefix"], summary["mask"])
+
+ _tag = summary.setdefault("tag", None)
+ if _tag:
+ cmd = "{} tag {}".format(cmd, _tag)
+
+ _advertise = summary.setdefault("advertise", True)
+ if not _advertise:
+ cmd = "{} no-advertise".format(cmd)
+
+ del_action = summary.setdefault("delete", False)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # ospf gr information
+ gr_data = ospf_data.setdefault("graceful-restart", {})
+ if gr_data:
+
+ if "opaque" in gr_data and gr_data["opaque"]:
+ cmd = "capability opaque"
+ if gr_data.setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if "helper enable" in gr_data and not gr_data["helper enable"]:
+ cmd = "graceful-restart helper enable"
+ if gr_data.setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+ elif "helper enable" in gr_data and type(gr_data["helper enable"]) is list:
+ for rtrs in gr_data["helper enable"]:
+ cmd = "graceful-restart helper enable {}".format(rtrs)
+ if gr_data.setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if "helper" in gr_data:
+ if type(gr_data["helper"]) is not list:
+ gr_data["helper"] = list(gr_data["helper"])
+ for helper_role in gr_data["helper"]:
+ cmd = "graceful-restart helper {}".format(helper_role)
+ if gr_data.setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if "supported-grace-time" in gr_data:
+ cmd = "graceful-restart helper supported-grace-time {}".format(
+ gr_data["supported-grace-time"]
+ )
+ if gr_data.setdefault("delete", False):
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ logger.debug("Exiting lib API: create_ospf_global()")
+
+ return config_data
+
+
+def config_ospf_interface(
+ tgen, topo=None, input_dict=None, build=False, load_config=True
+):
+ """
+ API to configure ospf on router.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ * `load_config` : Loading the config to router this is set as True.
+
+ Usage
+ -----
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10"
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Enter lib config_ospf_interface")
+ result = False
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ input_dict = deepcopy(input_dict)
+
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ config_data = []
+ for lnk in input_dict[router]["links"].keys():
+ if "ospf" not in input_dict[router]["links"][lnk]:
+ logger.debug(
+ "Router %s: ospf config is not present in" "input_dict", router
+ )
+ continue
+ ospf_data = input_dict[router]["links"][lnk]["ospf"]
+ data_ospf_area = ospf_data.setdefault("area", None)
+ data_ospf_auth = ospf_data.setdefault("authentication", None)
+ data_ospf_dr_priority = ospf_data.setdefault("priority", None)
+ data_ospf_cost = ospf_data.setdefault("cost", None)
+ data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
+
+ try:
+ intf = topo["routers"][router]["links"][lnk]["interface"]
+ except KeyError:
+ intf = topo["switches"][router]["links"][lnk]["interface"]
+
+ # interface
+ cmd = "interface {}".format(intf)
+
+ config_data.append(cmd)
+ # interface area config
+ if data_ospf_area:
+ cmd = "ip ospf area {}".format(data_ospf_area)
+ config_data.append(cmd)
+
+ # interface ospf auth
+ if data_ospf_auth:
+ if data_ospf_auth == "null":
+ cmd = "ip ospf authentication null"
+ elif data_ospf_auth == "message-digest":
+ cmd = "ip ospf authentication message-digest"
+ else:
+ cmd = "ip ospf authentication"
+
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if "message-digest-key" in ospf_data:
+ cmd = "ip ospf message-digest-key {} md5 {}".format(
+ ospf_data["message-digest-key"], ospf_data["authentication-key"]
+ )
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if (
+ "authentication-key" in ospf_data
+ and "message-digest-key" not in ospf_data
+ ):
+ cmd = "ip ospf authentication-key {}".format(
+ ospf_data["authentication-key"]
+ )
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf dr priority
+ if data_ospf_dr_priority:
+ cmd = "ip ospf priority {}".format(ospf_data["priority"])
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf cost
+ if data_ospf_cost:
+ cmd = "ip ospf cost {}".format(ospf_data["cost"])
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf mtu
+ if data_ospf_mtu:
+ cmd = "ip ospf mtu-ignore"
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if build:
+ return config_data
+
+ if config_data:
+ config_data_dict[router] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "interface_config", build=build
+ )
+
+ logger.debug("Exiting lib API: config_ospf_interface()")
+ return result
+
+
+def clear_ospf(tgen, router, ospf=None):
+ """
+ This API is to clear ospf neighborship by running
+ clear ip ospf interface * command,
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `router`: device under test
+
+ Usage
+ -----
+ clear_ospf(tgen, "r1")
+ """
+
+ logger.debug("Entering lib API: clear_ospf()")
+ if router not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[router]
+ # Clearing OSPF
+ if ospf:
+ version = "ipv6"
+ else:
+ version = "ip"
+
+ cmd = "clear {} ospf interface".format(version)
+ logger.info("Clearing ospf process on router %s.. using command '%s'", router, cmd)
+ run_frr_cmd(rnode, cmd)
+
+ logger.debug("Exiting lib API: clear_ospf()")
+
+
+def redistribute_ospf(tgen, topo, dut, route_type, **kwargs):
+ """
+ Redstribution of routes inside ospf.
+
+ Parameters
+ ----------
+ * `tgen`: Topogen object
+ * `topo` : json file data
+ * `dut`: device under test
+ * `route_type`: "static" or "connected" or ....
+ * `kwargs`: pass extra information (see below)
+
+ Usage
+ -----
+ redistribute_ospf(tgen, topo, "r0", "static", delete=True)
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+ """
+
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": route_type}]}}}
+ for k, v in kwargs.items():
+ ospf_red[dut]["ospf"]["redistribute"][0][k] = v
+
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+################################
+# Verification procs
+################################
+@retry(retry_timeout=80)
+def verify_ospf_neighbor(
+ tgen, topo=None, dut=None, input_dict=None, lan=False, expected=True
+):
+ """
+ This API is to verify ospf neighborship by running
+ show ip ospf neighbour command,
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `lan` : verify neighbors in lan topology
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ 1. To check FULL neighbors.
+ verify_ospf_neighbor(tgen, topo, dut=dut)
+
+ 2. To check neighbors with their roles.
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {
+ "state": "Full",
+ "role": "DR"
+ },
+ "r2": {
+ "state": "Full",
+ "role": "DROther"
+ },
+ "r3": {
+ "state": "Full",
+ "role": "DROther"
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+ logger.debug("Entering lib API: verify_ospf_neighbor()")
+ result = False
+ if topo is None:
+ topo = tgen.json_topo
+
+ if input_dict:
+ for router, rnode in tgen.routers().items():
+ if "ospf" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ip ospf neighbor all json", isjson=True
+ )
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ ospf_data_list = input_dict[router]["ospf"]
+ ospf_nbr_list = ospf_data_list["neighbors"]
+
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+ data_ip = topo["routers"][ospf_nbr]["links"]
+ data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"]
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo["switches"]:
+ if "ospf" in topo["switches"][switch]["links"][router]:
+ neighbor_ip = data_ip[switch]["ipv4"].split("/")[0]
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip[router]["ipv4"].split("/")[0]
+
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ try:
+ nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
+ intf_state = show_ospf_json[nbr_rid][0]["state"].split("/")[1]
+ except KeyError:
+ errormsg = "[DUT: {}] OSPF peer {} missing".format(router, nbr_rid)
+ return errormsg
+
+ nbr_state = nbr_data.setdefault("state", None)
+ nbr_role = nbr_data.setdefault("role", None)
+
+ if nbr_state:
+ if nbr_state == nh_state:
+ logger.info(
+ "[DUT: {}] OSPF Nbr is {}:{} State {}".format(
+ router, ospf_nbr, nbr_rid, nh_state
+ )
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF is not Converged, neighbor"
+ " state is {}".format(router, nh_state)
+ )
+ return errormsg
+ if nbr_role:
+ if nbr_role == intf_state:
+ logger.info(
+ "[DUT: {}] OSPF Nbr is {}: {} Role {}".format(
+ router, ospf_nbr, nbr_rid, nbr_role
+ )
+ )
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF is not Converged with rid"
+ "{}, role is {}".format(router, nbr_rid, intf_state)
+ )
+ return errormsg
+ continue
+ else:
+ for router, rnode in tgen.routers().items():
+ if "ospf" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ip ospf neighbor all json", isjson=True
+ )
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ ospf_data_list = topo["routers"][router]["ospf"]
+ ospf_neighbors = ospf_data_list["neighbors"]
+ total_peer = 0
+ total_peer = len(ospf_neighbors.keys())
+ no_of_ospf_nbr = 0
+ ospf_nbr_list = ospf_data_list["neighbors"]
+ no_of_peer = 0
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+ if nbr_data:
+ data_ip = topo["routers"][nbr_data["nbr"]]["links"]
+ data_rid = topo["routers"][nbr_data["nbr"]]["ospf"]["router_id"]
+ else:
+ data_ip = topo["routers"][ospf_nbr]["links"]
+ data_rid = topo["routers"][ospf_nbr]["ospf"]["router_id"]
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo["switches"]:
+ if "ospf" in topo["switches"][switch]["links"][router]:
+ neighbor_ip = data_ip[switch]["ipv4"].split("/")[0]
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip[router]["ipv4"].split("/")[0]
+
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ try:
+ nh_state = show_ospf_json[nbr_rid][0]["state"].split("/")[0]
+ except KeyError:
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
+ return errormsg
+
+ if nh_state == "Full":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer:
+ logger.info("[DUT: {}] OSPF is Converged".format(router))
+ result = True
+ else:
+ errormsg = "[DUT: {}] OSPF is not Converged".format(router)
+ return errormsg
+
+ logger.debug("Exiting API: verify_ospf_neighbor()")
+ return result
+
+
+################################
+# Verification procs
+################################
+@retry(retry_timeout=50)
+def verify_ospf6_neighbor(tgen, topo=None, dut=None, input_dict=None, lan=False):
+ """
+ This API is to verify ospf neighborship by running
+ show ipv6 ospf neighbour command,
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `lan` : verify neighbors in lan topology
+
+ Usage
+ -----
+ 1. To check FULL neighbors.
+ verify_ospf_neighbor(tgen, topo, dut=dut)
+
+ 2. To check neighbors with their roles.
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "neighbors": {
+ "r1": {
+ "state": "Full",
+ "role": "DR"
+ },
+ "r2": {
+ "state": "Full",
+ "role": "DROther"
+ },
+ "r3": {
+ "state": "Full",
+ "role": "DROther"
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_neighbor(tgen, topo, dut, input_dict, lan=True)
+
+ 3. To check there are no neighbors.
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "neighbors": []
+ }
+ }
+ }
+ result = verify_ospf6_neighbor(tgen, topo, dut, input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ if input_dict:
+ for router, rnode in tgen.routers().items():
+ if "ospf6" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ ospf_data_list = input_dict[router]["ospf6"]
+ ospf_nbr_list = ospf_data_list["neighbors"]
+
+ # Check if looking for no neighbors
+ if ospf_nbr_list == []:
+ if show_ospf_json["neighbors"] == []:
+ logger.info("[DUT: {}] OSPF6 no neighbors found".format(router))
+ return True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF6 active neighbors found, expected None".format(
+ router
+ )
+ )
+ return errormsg
+
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
+ neighbor_ip = data_ip
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip[router]["ipv6"].split("/")[0]
+
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
+ try:
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
+ except TypeError:
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
+ return errormsg
+
+ nbr_state = nbr_data.setdefault("state", None)
+ nbr_role = nbr_data.setdefault("role", None)
+
+ if nbr_state:
+ if nbr_state == nh_state:
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}:{} State {}".format(
+ router, ospf_nbr, nbr_rid, nh_state
+ )
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged, neighbor"
+ " state is {} , Expected state is {}".format(
+ router, nh_state, nbr_state
+ )
+ )
+ return errormsg
+ if nbr_role:
+ if nbr_role == intf_state:
+ logger.info(
+ "[DUT: {}] OSPF6 Nbr is {}: {} Role {}".format(
+ router, ospf_nbr, nbr_rid, nbr_role
+ )
+ )
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF6 is not Converged with rid"
+ "{}, role is {}, Expected role is {}".format(
+ router, nbr_rid, intf_state, nbr_role
+ )
+ )
+ return errormsg
+ continue
+ else:
+
+ for router, rnode in tgen.routers().items():
+ if "ospf6" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF6 neighborship on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf neighbor json", isjson=True
+ )
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ ospf_data_list = topo["routers"][router]["ospf6"]
+ ospf_neighbors = ospf_data_list["neighbors"]
+ total_peer = 0
+ total_peer = len(ospf_neighbors.keys())
+ no_of_ospf_nbr = 0
+ ospf_nbr_list = ospf_data_list["neighbors"]
+ no_of_peer = 0
+ for ospf_nbr, nbr_data in ospf_nbr_list.items():
+ try:
+ data_ip = data_rid = topo["routers"][ospf_nbr]["ospf6"]["router_id"]
+ except KeyError:
+ data_ip = data_rid = topo["routers"][nbr_data["nbr"]]["ospf6"][
+ "router_id"
+ ]
+
+ if ospf_nbr in data_ip:
+ nbr_details = nbr_data[ospf_nbr]
+ elif lan:
+ for switch in topo["switches"]:
+ if "ospf6" in topo["switches"][switch]["links"][router]:
+ neighbor_ip = data_ip
+ else:
+ continue
+ else:
+ neighbor_ip = data_ip
+
+ nh_state = None
+ neighbor_ip = neighbor_ip.lower()
+ nbr_rid = data_rid
+ get_index_val = dict(
+ (d["neighborId"], dict(d, index=index))
+ for (index, d) in enumerate(show_ospf_json["neighbors"])
+ )
+ try:
+ nh_state = get_index_val.get(neighbor_ip)["state"]
+ intf_state = get_index_val.get(neighbor_ip)["ifState"]
+ except TypeError:
+ errormsg = "[DUT: {}] OSPF peer {} missing,from " "{} ".format(
+ router, nbr_rid, ospf_nbr
+ )
+ return errormsg
+
+ if nh_state == "Full":
+ no_of_peer += 1
+
+ if no_of_peer == total_peer:
+ logger.info("[DUT: {}] OSPF6 is Converged".format(router))
+ result = True
+ else:
+ errormsg = "[DUT: {}] OSPF6 is not Converged".format(router)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=40)
+def verify_ospf_rib(
+ tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None, expected=True
+):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf route command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `next_hop` : next to be verified
+ * `tag` : tag to be verified
+ * `metric` : metric to be verified
+ * `fib` : True if the route is installed in FIB.
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_net,
+ "no_of_ip": 1,
+ "routeType": "N"
+ }
+ ]
+ }
+ }
+
+ result = verify_ospf_rib(tgen, dut, input_dict,next_hop=nh)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.info("Entering lib API: verify_ospf_rib()")
+ result = False
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s RIB:", router)
+
+ # Verifying RIB routes
+ command = "show ip ospf route"
+
+ found_routes = []
+ missing_routes = []
+
+ if (
+ "static_routes" in input_dict[routerInput]
+ or "prefix" in input_dict[routerInput]
+ ):
+ if "prefix" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["prefix"]
+ else:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ cmd = "{}".format(command)
+
+ cmd = "{} json".format(cmd)
+
+ ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Verifying output dictionary ospf_rib_json is not empty
+ if bool(ospf_rib_json) is False:
+ errormsg = (
+ "[DUT: {}] No routes found in OSPF route "
+ "table".format(router)
+ )
+ return errormsg
+
+ network = static_route["network"]
+ no_of_ip = static_route.setdefault("no_of_ip", 1)
+ _tag = static_route.setdefault("tag", None)
+ _rtype = static_route.setdefault("routeType", None)
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+ st_found = False
+ nh_found = False
+
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != "ipv4":
+ continue
+
+ if st_rt in ospf_rib_json:
+ st_found = True
+ found_routes.append(st_rt)
+
+ if fib and next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ for mnh in range(0, len(ospf_rib_json[st_rt])):
+ if (
+ "fib"
+ in ospf_rib_json[st_rt][mnh]["nexthops"][0]
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in ospf_rib_json[st_rt][mnh][
+ "nexthops"
+ ]
+ ]
+ )
+
+ if found_hops[0]:
+ missing_list_of_nexthops = set(
+ found_hops[0]
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops[0])
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Nexthop "
+ "%s is not active for route %s in "
+ "RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is not active"
+ " for route {} in RIB of router"
+ " {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+
+ elif next_hop and fib is None:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ found_hops = [
+ rib_r["ip"]
+ for rib_r in ospf_rib_json[st_rt]["nexthops"]
+ ]
+
+ if found_hops:
+ missing_list_of_nexthops = set(
+ found_hops
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops)
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+ if _rtype:
+ if "routeType" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: routeType missing"
+ " for route {} in OSPF RIB \n".format(
+ dut, st_rt
+ )
+ )
+ return errormsg
+ elif _rtype != ospf_rib_json[st_rt]["routeType"]:
+ errormsg = (
+ "[DUT: {}]: routeType mismatch"
+ " for route {} in OSPF RIB \n".format(
+ dut, st_rt
+ )
+ )
+ return errormsg
+ else:
+ logger.info(
+ "[DUT: {}]: Found routeType {}"
+ " for route {}".format(dut, _rtype, st_rt)
+ )
+ if tag:
+ if "tag" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if _tag != ospf_rib_json[st_rt]["tag"]:
+ errormsg = (
+ "[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ if metric is not None:
+ if "type2cost" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if metric != ospf_rib_json[st_rt]["type2cost"]:
+ errormsg = (
+ "[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info(
+ "[DUT: {}]: Found next_hop {} for all OSPF"
+ " routes in RIB".format(router, next_hop)
+ )
+
+ if len(missing_routes) > 0:
+ errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+ dut, missing_routes
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
+ result = True
+
+ logger.info("Exiting lib API: verify_ospf_rib()")
+ return result
+
+
+@retry(retry_timeout=20)
+def verify_ospf_interface(
+ tgen, topo=None, dut=None, lan=False, input_dict=None, expected=True
+):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf interface command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : topology descriptions
+ * `dut`: device under test
+ * `lan`: if set to true this interface belongs to LAN.
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict= {
+ 'r0': {
+ 'links':{
+ 's1': {
+ 'ospf':{
+ 'priority':98,
+ 'timerDeadSecs': 4,
+ 'area': '0.0.0.3',
+ 'mcastMemberOspfDesignatedRouters': True,
+ 'mcastMemberOspfAllRouters': True,
+ 'ospfEnabled': True,
+
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: verify_ospf_interface()")
+ result = False
+ if topo is None:
+ topo = tgen.json_topo
+
+ for router, rnode in tgen.routers().items():
+ if "ospf" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF interface on router %s:", router)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf interface json", isjson=True)
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ ospf_intf_data = input_dict[router]["links"]
+ for ospf_intf, intf_data in ospf_intf_data.items():
+ intf = topo["routers"][router]["links"][ospf_intf]["interface"]
+ if intf in show_ospf_json["interfaces"]:
+ for intf_attribute in intf_data["ospf"]:
+ if (
+ intf_data["ospf"][intf_attribute]
+ == show_ospf_json["interfaces"][intf][intf_attribute]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF interface %s: %s is %s",
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf"][intf_attribute],
+ )
+ else:
+ errormsg = "[DUT: {}] OSPF interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf"][intf_attribute],
+ show_ospf_json["interfaces"][intf][intf_attribute],
+ )
+ return errormsg
+ result = True
+ logger.debug("Exiting API: verify_ospf_interface()")
+ return result
+
+
+@retry(retry_timeout=20)
+def verify_ospf_database(tgen, topo, dut, input_dict, expected=True):
+ """
+ This API is to verify ospf lsa's by running
+ show ip ospf database command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `topo` : next to be verified
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "areas": {
+ "0.0.0.0": {
+ "Router Link States": {
+ "100.1.1.0-100.1.1.0": {
+ "LSID": "100.1.1.0",
+ "Advertised router": "100.1.1.0",
+ "LSA Age": 130,
+ "Sequence Number": "80000006",
+ "Checksum": "a703",
+ "Router links": 3
+ }
+ },
+ "Net Link States": {
+ "10.0.0.2-100.1.1.1": {
+ "LSID": "10.0.0.2",
+ "Advertised router": "100.1.1.1",
+ "LSA Age": 137,
+ "Sequence Number": "80000001",
+ "Checksum": "9583"
+ }
+ },
+ },
+ }
+ }
+ result = verify_ospf_database(tgen, topo, dut, input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ result = False
+ router = dut
+ logger.debug("Entering lib API: verify_ospf_database()")
+
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
+ return errormsg
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("Verifying OSPF interface on router %s:", dut)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ # for inter and inter lsa's
+ ospf_db_data = input_dict.setdefault("areas", None)
+ ospf_external_lsa = input_dict.setdefault("AS External Link States", None)
+ if ospf_db_data:
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json["areas"]:
+ if "Router Link States" in area_lsa:
+ for lsa in area_lsa["Router Link States"]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area]["Router Link States"]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Router LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+ if "Net Link States" in area_lsa:
+ for lsa in area_lsa["Net Link States"]:
+ if lsa in show_ospf_json["areas"][ospf_area]["Net Link States"]:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Network LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+ if "Summary Link States" in area_lsa:
+ for lsa in area_lsa["Summary Link States"]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area]["Summary Link States"]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+ if "ASBR-Summary Link States" in area_lsa:
+ for lsa in area_lsa["ASBR-Summary Link States"]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area][
+ "ASBR-Summary Link States"
+ ]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+ if ospf_external_lsa:
+ for ospf_ext_lsa, ext_lsa_data in ospf_external_lsa.items():
+ if ospf_ext_lsa in show_ospf_json["AS External Link States"]:
+ logger.info(
+ "[DUT: %s] OSPF LSDB:External LSA %s", router, ospf_ext_lsa
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB : expected"
+ " External LSA is {}".format(router, ospf_ext_lsa)
+ )
+ return errormsg
+
+ logger.debug("Exiting API: verify_ospf_database()")
+ return result
+
+
+@retry(retry_timeout=20)
+def verify_ospf_summary(tgen, topo, dut, input_dict, ospf=None, expected=True):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf interface command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : topology descriptions
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+
+ Usage
+ -----
+ input_dict = {
+ "11.0.0.0/8": {
+ "Summary address": "11.0.0.0/8",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5
+ }
+ }
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ router = dut
+
+ logger.info("Verifying OSPF summary on router %s:", router)
+
+ rnode = tgen.routers()[dut]
+
+ if ospf:
+ if "ospf6" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF6 is not configured on the router.".format(router)
+ return errormsg
+
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf summary detail json", isjson=True
+ )
+ else:
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(router)
+ return errormsg
+
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ip ospf summary detail json", isjson=True
+ )
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ ospf_summary_data = input_dict
+
+ if ospf:
+ show_ospf_json = show_ospf_json["default"]
+
+ for ospf_summ, summ_data in ospf_summary_data.items():
+ if ospf_summ not in show_ospf_json:
+ continue
+ summary = ospf_summary_data[ospf_summ]["Summary address"]
+
+ if summary in show_ospf_json:
+ for summ in summ_data:
+ if summ_data[summ] == show_ospf_json[summary][summ]:
+ logger.info(
+ "[DUT: %s] OSPF summary %s:%s is %s",
+ router,
+ summary,
+ summ,
+ summ_data[summ],
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF summary {} : {} is {}, "
+ "Expected is {}".format(
+ router,
+ summary,
+ summ,
+ show_ospf_json[summary][summ],
+ summ_data[summ],
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=30)
+def verify_ospf6_rib(
+ tgen, dut, input_dict, next_hop=None, tag=None, metric=None, fib=None
+):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf route command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `next_hop` : next to be verified
+ * `tag` : tag to be verified
+ * `metric` : metric to be verified
+ * `fib` : True if the route is installed in FIB.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_net,
+ "no_of_ip": 1,
+ "routeType": "N"
+ }
+ ]
+ }
+ }
+
+ result = verify_ospf6_rib(tgen, dut, input_dict,next_hop=nh)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ router_list = tgen.routers()
+ additional_nexthops_in_required_nhs = []
+ found_hops = []
+ for routerInput in input_dict.keys():
+ for router, rnode in router_list.items():
+ if router != dut:
+ continue
+
+ logger.info("Checking router %s RIB:", router)
+
+ # Verifying RIB routes
+ command = "show ipv6 ospf route detail"
+
+ found_routes = []
+ missing_routes = []
+
+ if (
+ "static_routes" in input_dict[routerInput]
+ or "prefix" in input_dict[routerInput]
+ ):
+ if "prefix" in input_dict[routerInput]:
+ static_routes = input_dict[routerInput]["prefix"]
+ else:
+ static_routes = input_dict[routerInput]["static_routes"]
+
+ for static_route in static_routes:
+ cmd = "{}".format(command)
+
+ cmd = "{} json".format(cmd)
+
+ ospf_rib_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ # Fix for PR 2644182
+ try:
+ ospf_rib_json = ospf_rib_json["routes"]
+ except KeyError:
+ pass
+
+ # Verifying output dictionary ospf_rib_json is not empty
+ if bool(ospf_rib_json) is False:
+ errormsg = (
+ "[DUT: {}] No routes found in OSPF6 route "
+ "table".format(router)
+ )
+ return errormsg
+
+ network = static_route["network"]
+ no_of_ip = static_route.setdefault("no_of_ip", 1)
+ _tag = static_route.setdefault("tag", None)
+ _rtype = static_route.setdefault("routeType", None)
+
+ # Generating IPs for verification
+ ip_list = generate_ips(network, no_of_ip)
+ if len(ip_list) == 1:
+ ip_list = [network]
+ st_found = False
+ nh_found = False
+ for st_rt in ip_list:
+ st_rt = str(ipaddress.ip_network(frr_unicode(st_rt)))
+
+ _addr_type = validate_ip_address(st_rt)
+ if _addr_type != "ipv6":
+ continue
+
+ if st_rt in ospf_rib_json:
+
+ st_found = True
+ found_routes.append(st_rt)
+
+ if fib and next_hop:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+
+ for mnh in range(0, len(ospf_rib_json[st_rt])):
+ if (
+ "fib"
+ in ospf_rib_json[st_rt][mnh]["nextHops"][0]
+ ):
+ found_hops.append(
+ [
+ rib_r["ip"]
+ for rib_r in ospf_rib_json[st_rt][mnh][
+ "nextHops"
+ ]
+ ]
+ )
+
+ if found_hops[0]:
+ missing_list_of_nexthops = set(
+ found_hops[0]
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops[0])
+
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Nexthop "
+ "%s is not active for route %s in "
+ "RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is not active"
+ " for route {} in RIB of router"
+ " {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+
+ elif next_hop and fib is None:
+ if type(next_hop) is not list:
+ next_hop = [next_hop]
+ found_hops = [
+ rib_r["nextHop"]
+ for rib_r in ospf_rib_json[st_rt]["nextHops"]
+ ]
+
+ if found_hops:
+ missing_list_of_nexthops = set(
+ found_hops
+ ).difference(next_hop)
+ additional_nexthops_in_required_nhs = set(
+ next_hop
+ ).difference(found_hops)
+ if additional_nexthops_in_required_nhs:
+ logger.info(
+ "Missing nexthop %s for route"
+ " %s in RIB of router %s\n",
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ errormsg = (
+ "Nexthop {} is Missing for "
+ "route {} in RIB of router {}\n".format(
+ additional_nexthops_in_required_nhs,
+ st_rt,
+ dut,
+ )
+ )
+ return errormsg
+ else:
+ nh_found = True
+ if _rtype:
+ if "destinationType" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: destinationType missing"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+ elif _rtype != ospf_rib_json[st_rt]["destinationType"]:
+ errormsg = (
+ "[DUT: {}]: destinationType mismatch"
+ "for route {} in OSPF RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+ else:
+ logger.info(
+ "DUT: {}]: Found destinationType {}"
+ "for route {}".format(dut, _rtype, st_rt)
+ )
+ if tag:
+ if "tag" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: tag is not"
+ " present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if _tag != ospf_rib_json[st_rt]["tag"]:
+ errormsg = (
+ "[DUT: {}]: tag value {}"
+ " is not matched for"
+ " route {} in RIB \n".format(
+ dut,
+ _tag,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ if metric is not None:
+ if "metricCostE2" not in ospf_rib_json[st_rt]:
+ errormsg = (
+ "[DUT: {}]: metric is"
+ " not present for"
+ " route {} in RIB \n".format(dut, st_rt)
+ )
+ return errormsg
+
+ if metric != ospf_rib_json[st_rt]["metricCostE2"]:
+ errormsg = (
+ "[DUT: {}]: metric value "
+ "{} is not matched for "
+ "route {} in RIB \n".format(
+ dut,
+ metric,
+ st_rt,
+ )
+ )
+ return errormsg
+
+ else:
+ missing_routes.append(st_rt)
+
+ if nh_found:
+ logger.info(
+ "[DUT: {}]: Found next_hop {} for all OSPF"
+ " routes in RIB".format(router, next_hop)
+ )
+
+ if len(missing_routes) > 0:
+ errormsg = "[DUT: {}]: Missing route in RIB, " "routes: {}".format(
+ dut, missing_routes
+ )
+ return errormsg
+
+ if found_routes:
+ logger.info(
+ "[DUT: %s]: Verified routes in RIB, found" " routes are: %s\n",
+ dut,
+ found_routes,
+ )
+ result = True
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=6)
+def verify_ospf6_interface(tgen, topo=None, dut=None, lan=False, input_dict=None):
+ """
+ This API is to verify ospf routes by running
+ show ip ospf interface command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : topology descriptions
+ * `dut`: device under test
+ * `lan`: if set to true this interface belongs to LAN.
+ * `input_dict` : Input dict data, required when configuring from testcase
+
+ Usage
+ -----
+ input_dict= {
+ 'r0': {
+ 'links':{
+ 's1': {
+ 'ospf6':{
+ 'priority':98,
+ 'timerDeadSecs': 4,
+ 'area': '0.0.0.3',
+ 'mcastMemberOspfDesignatedRouters': True,
+ 'mcastMemberOspfAllRouters': True,
+ 'ospfEnabled': True,
+
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ logger.debug("Entering lib API: verify_ospf6_interface")
+ result = False
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ for router, rnode in tgen.routers().items():
+ if "ospf6" not in topo["routers"][router]:
+ continue
+
+ if dut is not None and dut != router:
+ continue
+
+ logger.info("Verifying OSPF interface on router %s:", router)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ipv6 ospf interface json", isjson=True
+ )
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF6 is not running"
+ return errormsg
+
+ # To find neighbor ip type
+ ospf_intf_data = input_dict[router]["links"]
+ for ospf_intf, intf_data in ospf_intf_data.items():
+ intf = topo["routers"][router]["links"][ospf_intf]["interface"]
+ if intf in show_ospf_json:
+ for intf_attribute in intf_data["ospf6"]:
+ if intf_data["ospf6"][intf_attribute] is not list:
+ if (
+ intf_data["ospf6"][intf_attribute]
+ == show_ospf_json[intf][intf_attribute]
+ ):
+ logger.info(
+ "[DUT: %s] OSPF6 interface %s: %s is %s",
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ )
+ elif intf_data["ospf6"][intf_attribute] is list:
+ for addr_list in len(show_ospf_json[intf][intf_attribute]):
+ if (
+ show_ospf_json[intf][intf_attribute][addr_list][
+ "address"
+ ].split("/")[0]
+ == intf_data["ospf6"]["internetAddress"][0]["address"]
+ ):
+ break
+ else:
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
+ return errormsg
+ else:
+ errormsg = "[DUT: {}] OSPF6 interface {}: {} is {}, \
+ Expected is {}".format(
+ router,
+ intf,
+ intf_attribute,
+ intf_data["ospf6"][intf_attribute],
+ intf_data["ospf6"][intf_attribute],
+ )
+ return errormsg
+ result = True
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=20)
+def verify_ospf6_database(tgen, topo, dut, input_dict):
+ """
+ This API is to verify ospf lsa's by running
+ show ip ospf database command.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `dut`: device under test
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `topo` : next to be verified
+
+ Usage
+ -----
+ input_dict = {
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": {
+ "100.1.1.0-100.1.1.0": {
+ "LSID": "100.1.1.0",
+ "Advertised router": "100.1.1.0",
+ "LSA Age": 130,
+ "Sequence Number": "80000006",
+ "Checksum": "a703",
+ "Router links": 3
+ }
+ },
+ "networkLinkStates": {
+ "10.0.0.2-100.1.1.1": {
+ "LSID": "10.0.0.2",
+ "Advertised router": "100.1.1.1",
+ "LSA Age": 137,
+ "Sequence Number": "80000001",
+ "Checksum": "9583"
+ }
+ },
+ },
+ }
+ }
+ result = verify_ospf_database(tgen, topo, dut, input_dict)
+
+ Returns
+ -------
+ True or False (Error Message)
+ """
+
+ result = False
+ router = dut
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
+ return errormsg
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("Verifying OSPF interface on router %s:", dut)
+ show_ospf_json = run_frr_cmd(rnode, "show ip ospf database json", isjson=True)
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ return errormsg
+
+ # for inter and inter lsa's
+ ospf_db_data = input_dict.setdefault("areas", None)
+ ospf_external_lsa = input_dict.setdefault("asExternalLinkStates", None)
+
+ if ospf_db_data:
+ for ospf_area, area_lsa in ospf_db_data.items():
+ if ospf_area in show_ospf_json["areas"]:
+ if "routerLinkStates" in area_lsa:
+ for lsa in area_lsa["routerLinkStates"]:
+ for rtrlsa in show_ospf_json["areas"][ospf_area][
+ "routerLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == rtrlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == rtrlsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Router " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Router LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+
+ if "networkLinkStates" in area_lsa:
+ for lsa in area_lsa["networkLinkStates"]:
+ for netlsa in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]:
+ if (
+ lsa
+ in show_ospf_json["areas"][ospf_area][
+ "networkLinkStates"
+ ]
+ ):
+ if (
+ lsa["lsaId"] == netlsa["lsaId"]
+ and lsa["advertisedRouter"]
+ == netlsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Network " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Network LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+
+ if "summaryLinkStates" in area_lsa:
+ for lsa in area_lsa["summaryLinkStates"]:
+ for t3lsa in show_ospf_json["areas"][ospf_area][
+ "summaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t3lsa["lsaId"]
+ and lsa["advertisedRouter"] == t3lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+
+ if "nssaExternalLinkStates" in area_lsa:
+ for lsa in area_lsa["nssaExternalLinkStates"]:
+ for t7lsa in show_ospf_json["areas"][ospf_area][
+ "nssaExternalLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t7lsa["lsaId"]
+ and lsa["advertisedRouter"] == t7lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:Type7 " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ break
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " Type7 LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+
+ if "asbrSummaryLinkStates" in area_lsa:
+ for lsa in area_lsa["asbrSummaryLinkStates"]:
+ for t4lsa in show_ospf_json["areas"][ospf_area][
+ "asbrSummaryLinkStates"
+ ]:
+ if (
+ lsa["lsaId"] == t4lsa["lsaId"]
+ and lsa["advertisedRouter"] == t4lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ if result:
+ logger.info(
+ "[DUT: %s] OSPF LSDB area %s:ASBR Summary " "LSA %s",
+ router,
+ ospf_area,
+ lsa,
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB area {}: expected"
+ " ASBR Summary LSA is {}".format(router, ospf_area, lsa)
+ )
+ return errormsg
+
+ if "linkLocalOpaqueLsa" in area_lsa:
+ for lsa in area_lsa["linkLocalOpaqueLsa"]:
+ try:
+ for lnklsa in show_ospf_json["areas"][ospf_area][
+ "linkLocalOpaqueLsa"
+ ]:
+ if (
+ lsa["lsaId"] in lnklsa["lsaId"]
+ and "linkLocalOpaqueLsa"
+ in show_ospf_json["areas"][ospf_area]
+ ):
+ logger.info(
+ (
+ "[DUT: FRR] OSPF LSDB area %s:Opaque-LSA"
+ "%s",
+ ospf_area,
+ lsa,
+ )
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: FRR] OSPF LSDB area: {} "
+ "expected Opaque-LSA is {}, Found is {}".format(
+ ospf_area, lsa, show_ospf_json
+ )
+ )
+ raise ValueError(errormsg)
+ return errormsg
+ except KeyError:
+ errormsg = "[DUT: FRR] linkLocalOpaqueLsa Not " "present"
+ return errormsg
+
+ if ospf_external_lsa:
+ for lsa in ospf_external_lsa:
+ try:
+ for t5lsa in show_ospf_json["asExternalLinkStates"]:
+ if (
+ lsa["lsaId"] == t5lsa["lsaId"]
+ and lsa["advertisedRouter"] == t5lsa["advertisedRouter"]
+ ):
+ result = True
+ break
+ except KeyError:
+ result = False
+ if result:
+ logger.info("[DUT: %s] OSPF LSDB:External LSA %s", router, lsa)
+ result = True
+ else:
+ errormsg = (
+ "[DUT: {}] OSPF LSDB : expected"
+ " External LSA is {}".format(router, lsa)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def config_ospf6_interface(
+ tgen, topo=None, input_dict=None, build=False, load_config=True
+):
+ """
+ API to configure ospf on router.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+ * `load_config` : Loading the config to router this is set as True.
+
+ Usage
+ -----
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": 'message-digest',
+ "authentication-key": "ospf",
+ "message-digest-key": "10"
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf_auth)
+
+ Returns
+ -------
+ True or False
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ if topo is None:
+ topo = tgen.json_topo
+
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ input_dict = deepcopy(input_dict)
+
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ config_data = []
+ for lnk in input_dict[router]["links"].keys():
+ if "ospf6" not in input_dict[router]["links"][lnk]:
+ logger.debug(
+ "Router %s: ospf6 config is not present in"
+ "input_dict, passed input_dict %s",
+ router,
+ str(input_dict),
+ )
+ continue
+ ospf_data = input_dict[router]["links"][lnk]["ospf6"]
+ data_ospf_area = ospf_data.setdefault("area", None)
+ data_ospf_auth = ospf_data.setdefault("hash-algo", None)
+ data_ospf_keychain = ospf_data.setdefault("keychain", None)
+ data_ospf_dr_priority = ospf_data.setdefault("priority", None)
+ data_ospf_cost = ospf_data.setdefault("cost", None)
+ data_ospf_mtu = ospf_data.setdefault("mtu_ignore", None)
+
+ try:
+ intf = topo["routers"][router]["links"][lnk]["interface"]
+ except KeyError:
+ intf = topo["switches"][router]["links"][lnk]["interface"]
+
+ # interface
+ cmd = "interface {}".format(intf)
+
+ config_data.append(cmd)
+ # interface area config
+ if data_ospf_area:
+ cmd = "ipv6 ospf area {}".format(data_ospf_area)
+ config_data.append(cmd)
+
+ # interface ospf auth
+ if data_ospf_auth:
+ cmd = "ipv6 ospf6 authentication"
+
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+
+ if "hash-algo" in ospf_data:
+ cmd = "{} key-id {} hash-algo {} key {}".format(
+ cmd,
+ ospf_data["key-id"],
+ ospf_data["hash-algo"],
+ ospf_data["key"],
+ )
+ config_data.append(cmd)
+
+ # interface ospf auth with keychain
+ if data_ospf_keychain:
+ cmd = "ipv6 ospf6 authentication"
+
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+
+ if "keychain" in ospf_data:
+ cmd = "{} keychain {}".format(cmd, ospf_data["keychain"])
+ config_data.append(cmd)
+
+ # interface ospf dr priority
+ if data_ospf_dr_priority:
+ cmd = "ipv6 ospf priority {}".format(ospf_data["priority"])
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf cost
+ if data_ospf_cost:
+ cmd = "ipv6 ospf cost {}".format(ospf_data["cost"])
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # interface ospf mtu
+ if data_ospf_mtu:
+ cmd = "ipv6 ospf mtu-ignore"
+ if "del_action" in ospf_data:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if build:
+ return config_data
+
+ if config_data:
+ config_data_dict[router] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "interface_config", build=build
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+@retry(retry_timeout=20)
+def verify_ospf_gr_helper(tgen, topo, dut, input_dict=None):
+ """
+ This API is used to vreify gr helper using command
+ show ip ospf graceful-restart helper
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : topology descriptions
+ * 'dut' : router
+ * 'input_dict' - values to be verified
+
+ Usage:
+ -------
+ input_dict = {
+ "helperSupport":"Disabled",
+ "strictLsaCheck":"Enabled",
+ "restartSupoort":"Planned and Unplanned Restarts",
+ "supportedGracePeriod":1800
+ }
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+
+ if "ospf" not in topo["routers"][dut]:
+ errormsg = "[DUT: {}] OSPF is not configured on the router.".format(dut)
+ return errormsg
+
+ rnode = tgen.routers()[dut]
+ logger.info("Verifying OSPF GR details on router %s:", dut)
+ show_ospf_json = run_frr_cmd(
+ rnode, "show ip ospf graceful-restart helper json", isjson=True
+ )
+
+ # Verifying output dictionary show_ospf_json is empty or not
+ if not bool(show_ospf_json):
+ errormsg = "OSPF is not running"
+ raise ValueError(errormsg)
+ return errormsg
+
+ for ospf_gr, gr_data in input_dict.items():
+ try:
+ if input_dict[ospf_gr] == show_ospf_json[ospf_gr]:
+ logger.info(
+ "[DUT: FRR] OSPF GR Helper: %s is %s",
+ ospf_gr,
+ show_ospf_json[ospf_gr],
+ )
+ result = True
+ else:
+ errormsg = (
+ "[DUT: FRR] OSPF GR Helper: {} expected is {}, Found "
+ "is {}".format(
+ ospf_gr, input_dict[ospf_gr], show_ospf_json[ospf_gr]
+ )
+ )
+ raise ValueError(errormsg)
+ return errormsg
+
+ except KeyError:
+ errormsg = "[DUT: FRR] OSPF GR Helper: {}".format(ospf_gr)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
diff --git a/tests/topotests/lib/pim.py b/tests/topotests/lib/pim.py
new file mode 100644
index 0000000..03ab024
--- /dev/null
+++ b/tests/topotests/lib/pim.py
@@ -0,0 +1,3979 @@
+# Copyright (c) 2019 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.
+
+import datetime
+import os
+import re
+import sys
+import traceback
+import functools
+from copy import deepcopy
+from time import sleep
+from lib import topotest
+
+
+# Import common_config to use commomnly used APIs
+from lib.common_config import (
+ create_common_configurations,
+ HostApplicationHelper,
+ InvalidCLIError,
+ create_common_configuration,
+ InvalidCLIError,
+ retry,
+ run_frr_cmd,
+ validate_ip_address,
+)
+from lib.micronet import get_exec_path
+from lib.topolog import logger
+from lib.topotest import frr_unicode
+
+####
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def create_pim_config(tgen, topo, input_dict=None, build=False, load_config=True):
+ """
+ API to configure pim/pimv6 on router
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from
+ testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "pim": {
+ "join-prune-interval": "5",
+ "rp": [{
+ "rp_addr" : "1.0.3.17".
+ "keep-alive-timer": "100"
+ "group_addr_range": ["224.1.1.0/24", "225.1.1.0/24"]
+ "prefix-list": "pf_list_1"
+ "delete": True
+ }]
+ },
+ "pim6": {
+ "disable" : ["l1-i1-eth1"],
+ "rp": [{
+ "rp_addr" : "2001:db8:f::5:17".
+ "keep-alive-timer": "100"
+ "group_addr_range": ["FF00::/8"]
+ "prefix-list": "pf_list_1"
+ "delete": True
+ }]
+ }
+ }
+ }
+
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ topo = topo["routers"]
+ input_dict = deepcopy(input_dict)
+
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ config_data = _enable_disable_pim_config(tgen, topo, input_dict, router, build)
+
+ if config_data:
+ config_data_dict[router] = config_data
+
+ # Now add RP config to all routers
+ for router in input_dict.keys():
+ if "pim" in input_dict[router] or "pim6" in input_dict[router]:
+ _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict)
+ try:
+ result = create_common_configurations(
+ tgen, config_data_dict, "pim", build, load_config
+ )
+ except InvalidCLIError:
+ logger.error("create_pim_config", exc_info=True)
+ result = False
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def _add_pim_rp_config(tgen, topo, input_dict, router, build, config_data_dict):
+ """
+ Helper API to create pim RP configurations.
+
+ 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.
+ * `config_data_dict` : OUT: adds `router` config to dictinary
+ Returns
+ -------
+ None
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ rp_data = []
+
+ # PIMv4
+ pim_data = None
+ if "pim" in input_dict[router]:
+ pim_data = input_dict[router]["pim"]
+ if "rp" in input_dict[router]["pim"]:
+ rp_data += pim_data["rp"]
+
+ # PIMv6
+ pim6_data = None
+ if "pim6" in input_dict[router]:
+ pim6_data = input_dict[router]["pim6"]
+ if "rp" in input_dict[router]["pim6"]:
+ rp_data += pim6_data["rp"]
+
+ # Configure this RP on every router.
+ for dut in tgen.routers():
+ # At least one interface must be enabled for PIM on the router
+ pim_if_enabled = False
+ pim6_if_enabled = False
+ for destLink, data in topo[dut]["links"].items():
+ if "pim" in data:
+ pim_if_enabled = True
+ if "pim6" in data:
+ pim6_if_enabled = True
+ if not pim_if_enabled and pim_data:
+ continue
+ if not pim6_if_enabled and pim6_data:
+ continue
+
+ config_data = []
+
+ if rp_data:
+ for rp_dict in deepcopy(rp_data):
+ # ip address of RP
+ if "rp_addr" not in rp_dict and build:
+ logger.error(
+ "Router %s: 'ip address of RP' not "
+ "present in input_dict/JSON",
+ router,
+ )
+
+ return False
+ rp_addr = rp_dict.setdefault("rp_addr", None)
+ if rp_addr:
+ addr_type = validate_ip_address(rp_addr)
+ # Keep alive Timer
+ keep_alive_timer = rp_dict.setdefault("keep_alive_timer", None)
+
+ # Group Address range to cover
+ if "group_addr_range" not in rp_dict and build:
+ logger.error(
+ "Router %s:'Group Address range to cover'"
+ " not present in input_dict/JSON",
+ router,
+ )
+
+ return False
+ group_addr_range = rp_dict.setdefault("group_addr_range", None)
+
+ # Group prefix-list filter
+ prefix_list = rp_dict.setdefault("prefix_list", None)
+
+ # Delete rp config
+ del_action = rp_dict.setdefault("delete", False)
+
+ if keep_alive_timer:
+ if addr_type == "ipv4":
+ cmd = "ip pim rp keep-alive-timer {}".format(keep_alive_timer)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+ if addr_type == "ipv6":
+ cmd = "ipv6 pim rp keep-alive-timer {}".format(keep_alive_timer)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if rp_addr:
+ if group_addr_range:
+ if type(group_addr_range) is not list:
+ group_addr_range = [group_addr_range]
+
+ for grp_addr in group_addr_range:
+ if addr_type == "ipv4":
+ cmd = "ip pim rp {} {}".format(rp_addr, grp_addr)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+ if addr_type == "ipv6":
+ cmd = "ipv6 pim rp {} {}".format(rp_addr, grp_addr)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if prefix_list:
+ if addr_type == "ipv4":
+ cmd = "ip pim rp {} prefix-list {}".format(
+ rp_addr, prefix_list
+ )
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+ if addr_type == "ipv6":
+ cmd = "ipv6 pim rp {} prefix-list {}".format(
+ rp_addr, prefix_list
+ )
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if config_data:
+ if dut not in config_data_dict:
+ config_data_dict[dut] = config_data
+ else:
+ config_data_dict[dut].extend(config_data)
+
+
+def create_igmp_config(tgen, topo, input_dict=None, build=False):
+ """
+ API to configure igmp on router
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from
+ testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ "r1-r0-eth0" :{
+ "igmp":{
+ "version": "2",
+ "delete": True
+ "query": {
+ "query-interval" : 100,
+ "query-max-response-time": 200
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ topo = topo["routers"]
+ input_dict = deepcopy(input_dict)
+
+ config_data_dict = {}
+
+ for router in input_dict.keys():
+ if "igmp" not in input_dict[router]:
+ logger.debug("Router %s: 'igmp' is not present in " "input_dict", router)
+ continue
+
+ igmp_data = input_dict[router]["igmp"]
+
+ if "interfaces" in igmp_data:
+ config_data = []
+ intf_data = igmp_data["interfaces"]
+
+ for intf_name in intf_data.keys():
+ cmd = "interface {}".format(intf_name)
+ config_data.append(cmd)
+ protocol = "igmp"
+ del_action = intf_data[intf_name]["igmp"].setdefault("delete", False)
+ del_attr = intf_data[intf_name]["igmp"].setdefault("delete_attr", False)
+ cmd = "ip igmp"
+ if del_action:
+ cmd = "no {}".format(cmd)
+ if not del_attr:
+ config_data.append(cmd)
+
+ for attribute, data in intf_data[intf_name]["igmp"].items():
+ if attribute == "version":
+ cmd = "ip {} {} {}".format(protocol, attribute, data)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ if not del_attr:
+ config_data.append(cmd)
+
+ if attribute == "join":
+ for group in data:
+ cmd = "ip {} {} {}".format(protocol, attribute, group)
+ if del_attr:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if attribute == "query":
+ for query, value in data.items():
+ if query != "delete":
+ cmd = "ip {} {} {}".format(protocol, query, value)
+
+ if "delete" in intf_data[intf_name][protocol]["query"]:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+ if config_data:
+ config_data_dict[router] = config_data
+
+ try:
+ result = create_common_configurations(
+ tgen, config_data_dict, "interface_config", build=build
+ )
+ except InvalidCLIError:
+ logger.error("create_igmp_config", exc_info=True)
+ result = False
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def create_mld_config(tgen, topo, input_dict=None, build=False):
+ """
+ API to configure mld for PIMv6 on router
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from
+ testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "mld": {
+ "interfaces": {
+ "r1-r0-eth0" :{
+ "mld":{
+ "version": "2",
+ "delete": True
+ "query": {
+ "query-interval" : 100,
+ "query-max-response-time": 200
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ Returns
+ -------
+ True or False
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ result = False
+ if not input_dict:
+ input_dict = deepcopy(topo)
+ else:
+ topo = topo["routers"]
+ input_dict = deepcopy(input_dict)
+ for router in input_dict.keys():
+ if "mld" not in input_dict[router]:
+ logger.debug("Router %s: 'mld' is not present in " "input_dict", router)
+ continue
+
+ mld_data = input_dict[router]["mld"]
+
+ if "interfaces" in mld_data:
+ config_data = []
+ intf_data = mld_data["interfaces"]
+
+ for intf_name in intf_data.keys():
+ cmd = "interface {}".format(intf_name)
+ config_data.append(cmd)
+ protocol = "mld"
+ del_action = intf_data[intf_name]["mld"].setdefault("delete", False)
+ cmd = "ipv6 mld"
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ del_attr = intf_data[intf_name]["mld"].setdefault("delete_attr", False)
+ join = intf_data[intf_name]["mld"].setdefault("join", None)
+ source = intf_data[intf_name]["mld"].setdefault("source", None)
+ version = intf_data[intf_name]["mld"].setdefault("version", False)
+ query = intf_data[intf_name]["mld"].setdefault("query", {})
+
+ if version:
+ cmd = "ipv6 {} version {}".format(protocol, version)
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if source and join:
+ for group in join:
+ cmd = "ipv6 {} join {} {}".format(protocol, group, source)
+
+ if del_attr:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ elif join:
+ for group in join:
+ cmd = "ipv6 {} join {}".format(protocol, group)
+
+ if del_attr:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ if query:
+ for _query, value in query.items():
+ if _query != "delete":
+ cmd = "ipv6 {} {} {}".format(protocol, _query, value)
+
+ if "delete" in intf_data[intf_name][protocol]["query"]:
+ cmd = "no {}".format(cmd)
+
+ config_data.append(cmd)
+ try:
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=build
+ )
+ except InvalidCLIError:
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+def _enable_disable_pim_config(tgen, topo, input_dict, router, build=False):
+ """
+ Helper API to enable or disable pim on interfaces
+
+ 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.
+
+ Returns
+ -------
+ list of config
+ """
+
+ config_data = []
+
+ # Enable pim/pim6 on interfaces
+ for destRouterLink, data in sorted(topo[router]["links"].items()):
+ if "pim" in data and data["pim"] == "enable":
+ # Loopback interfaces
+ if "type" in data and data["type"] == "loopback":
+ interface_name = destRouterLink
+ else:
+ interface_name = data["interface"]
+
+ cmd = "interface {}".format(interface_name)
+ config_data.append(cmd)
+ config_data.append("ip pim")
+
+ if "pim6" in data and data["pim6"] == "enable":
+ # Loopback interfaces
+ if "type" in data and data["type"] == "loopback":
+ interface_name = destRouterLink
+ else:
+ interface_name = data["interface"]
+
+ cmd = "interface {}".format(interface_name)
+ config_data.append(cmd)
+ config_data.append("ipv6 pim")
+
+ # pim global config
+ if "pim" in input_dict[router]:
+ pim_data = input_dict[router]["pim"]
+ del_action = pim_data.setdefault("delete", False)
+ for t in [
+ "join-prune-interval",
+ "keep-alive-timer",
+ "register-suppress-time",
+ ]:
+ if t in pim_data:
+ cmd = "ip pim {} {}".format(t, pim_data[t])
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ # pim6 global config
+ if "pim6" in input_dict[router]:
+ pim6_data = input_dict[router]["pim6"]
+ del_action = pim6_data.setdefault("delete", False)
+ for t in [
+ "join-prune-interval",
+ "keep-alive-timer",
+ "register-suppress-time",
+ ]:
+ if t in pim6_data:
+ cmd = "ipv6 pim {} {}".format(t, pim6_data[t])
+ if del_action:
+ cmd = "no {}".format(cmd)
+ config_data.append(cmd)
+
+ return config_data
+
+
+def find_rp_details(tgen, topo):
+ """
+ Find who is RP in topology and returns list of RPs
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ rp_details = {}
+
+ router_list = tgen.routers()
+ topo_data = topo["routers"]
+
+ for router in router_list.keys():
+
+ if "pim" not in topo_data[router]:
+ continue
+
+ pim_data = topo_data[router]["pim"]
+ if "rp" in pim_data:
+ rp_data = pim_data["rp"]
+ for rp_dict in rp_data:
+ # ip address of RP
+ rp_addr = rp_dict["rp_addr"]
+
+ for link, data in topo["routers"][router]["links"].items():
+ if data["ipv4"].split("/")[0] == rp_addr:
+ rp_details[router] = rp_addr
+
+ return rp_details
+
+
+def configure_pim_force_expire(tgen, topo, input_dict, build=False):
+ """
+ Helper API to create pim configuration.
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `input_dict` : Input dict data, required when configuring from testcase
+ * `build` : Only for initial setup phase this is set as True.
+
+ Usage
+ -----
+ input_dict ={
+ "l1": {
+ "pim": {
+ "force_expire":{
+ "10.0.10.1": ["255.1.1.1"]
+ }
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+
+ Returns
+ -------
+ True or False
+ """
+
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data_dict = {}
+
+ for dut in input_dict.keys():
+ if "pim" not in input_dict[dut]:
+ continue
+
+ pim_data = input_dict[dut]["pim"]
+
+ config_data = []
+ if "force_expire" in pim_data:
+ force_expire_data = pim_data["force_expire"]
+
+ for source, groups in force_expire_data.items():
+ if type(groups) is not list:
+ groups = [groups]
+
+ for group in groups:
+ cmd = "ip pim force-expire source {} group {}".format(
+ source, group
+ )
+ config_data.append(cmd)
+
+ if config_data:
+ config_data_dict[dut] = config_data
+
+ result = create_common_configurations(
+ tgen, config_data_dict, "pim", build=build
+ )
+ except InvalidCLIError:
+ logger.error("configure_pim_force_expire", exc_info=True)
+ result = False
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return result
+
+
+#############################################
+# Verification APIs
+#############################################
+@retry(retry_timeout=12)
+def verify_pim_neighbors(tgen, topo, dut=None, iface=None, nbr_ip=None, expected=True):
+ """
+ Verify all PIM neighbors are up and running, config is verified
+ using "show ip pim neighbor" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : dut info
+ * `iface` : link for which PIM nbr need to check
+ * `nbr_ip` : neighbor ip of interface
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_pim_neighbors(tgen, topo, dut, iface=ens192, nbr_ip=20.1.1.2)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in tgen.routers():
+ if dut is not None and dut != router:
+ continue
+
+ rnode = tgen.routers()[router]
+ show_ip_pim_neighbor_json = rnode.vtysh_cmd(
+ "show ip pim neighbor json", isjson=True
+ )
+
+ for destLink, data in topo["routers"][router]["links"].items():
+ if iface is not None and iface != data["interface"]:
+ continue
+
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" not in data:
+ continue
+
+ if "pim" in data and data["pim"] == "disable":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ local_interface = data["interface"]
+
+ if "-" in destLink:
+ # Spliting and storing destRouterLink data in tempList
+ tempList = destLink.split("-")
+
+ # destRouter
+ destLink = tempList.pop(0)
+
+ # Current Router Link
+ tempList.insert(0, router)
+ curRouter = "-".join(tempList)
+ else:
+ curRouter = router
+ if destLink not in topo["routers"]:
+ continue
+ data = topo["routers"][destLink]["links"][curRouter]
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" not in data:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM neighbor status:", router)
+
+ if "pim" in data and data["pim"] == "enable":
+ pim_nh_intf_ip = data["ipv4"].split("/")[0]
+
+ # Verifying PIM neighbor
+ if local_interface in show_ip_pim_neighbor_json:
+ if show_ip_pim_neighbor_json[local_interface]:
+ if (
+ show_ip_pim_neighbor_json[local_interface][pim_nh_intf_ip][
+ "neighbor"
+ ]
+ != pim_nh_intf_ip
+ ):
+ errormsg = (
+ "[DUT %s]: Local interface: %s, PIM"
+ " neighbor check failed "
+ "Expected neighbor: %s, Found neighbor:"
+ " %s"
+ % (
+ router,
+ local_interface,
+ pim_nh_intf_ip,
+ show_ip_pim_neighbor_json[local_interface][
+ pim_nh_intf_ip
+ ]["neighbor"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Local interface: %s, Found"
+ " expected PIM neighbor %s",
+ router,
+ local_interface,
+ pim_nh_intf_ip,
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Local interface: %s, and"
+ "interface ip: %s is not found in "
+ "PIM neighbor " % (router, local_interface, pim_nh_intf_ip)
+ )
+ return errormsg
+ else:
+ errormsg = (
+ "[DUT %s]: Local interface: %s, is not "
+ "present in PIM neighbor " % (router, local_interface)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=40, diag_pct=0)
+def verify_igmp_groups(tgen, dut, interface, group_addresses, expected=True):
+ """
+ Verify IGMP groups are received from an intended interface
+ by running "show ip igmp groups" command
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `interface`: interface, from which IGMP groups would be received
+ * `group_addresses`: IGMP group address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_igmp_groups(tgen, dut, interface, group_address)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying IGMP groups received:", dut)
+ show_ip_igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if interface in show_ip_igmp_json:
+ show_ip_igmp_json = show_ip_igmp_json[interface]["groups"]
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying IGMP group received"
+ " from interface %s [FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ found = False
+ for grp_addr in group_addresses:
+ for index in show_ip_igmp_json:
+ if index["group"] == grp_addr:
+ found = True
+ break
+ if found is not True:
+ errormsg = (
+ "[DUT %s]: Verifying IGMP group received"
+ " from interface %s [FAILED]!! "
+ " Expected not found: %s" % (dut, interface, grp_addr)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying IGMP group %s received "
+ "from interface %s [PASSED]!! ",
+ dut,
+ grp_addr,
+ interface,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_upstream_iif(
+ tgen,
+ dut,
+ iif,
+ src_address,
+ group_addresses,
+ joinState=None,
+ refCount=1,
+ expected=True,
+):
+ """
+ Verify upstream inbound interface is updated correctly
+ by running "show ip pim upstream" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `iif`: inbound interface
+ * `src_address`: source address
+ * `group_addresses`: IGMP group address
+ * `joinState`: upstream join state
+ * `refCount`: refCount value
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ iif = "r1-r0-eth0"
+ src_address = "*"
+ group_address = "225.1.1.1"
+ result = verify_upstream_iif(tgen, dut, iif, src_address, group_address,
+ state, refCount)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info(
+ "[DUT: %s]: Verifying upstream Inbound Interface" " for IGMP groups received:",
+ dut,
+ )
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if type(iif) is not list:
+ iif = [iif]
+
+ for grp in group_addresses:
+ addr_type = validate_ip_address(grp)
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ cmd = "show {} pim upstream json".format(ip_cmd)
+ show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ for grp_addr in group_addresses:
+ # Verify group address
+ if grp_addr not in show_ip_pim_upstream_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
+ dut,
+ grp_addr,
+ )
+ return errormsg
+ group_addr_json = show_ip_pim_upstream_json[grp_addr]
+
+ # Verify source address
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+
+ # Verify Inbound Interface
+ found = False
+ for in_interface in iif:
+ if group_addr_json[src_address]["inboundInterface"] == in_interface:
+ if refCount > 0:
+ logger.info(
+ "[DUT %s]: Verifying refCount "
+ "for (%s,%s) [PASSED]!! "
+ " Found Expected: %s",
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["refCount"],
+ )
+ found = True
+ if found:
+ if joinState is None:
+ if group_addr_json[src_address]["joinState"] != "Joined":
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s,%s) and"
+ " joinState :%s [FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ elif group_addr_json[src_address]["joinState"] != joinState:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s,%s) and"
+ " joinState :%s [FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying iif(Inbound Interface)"
+ " for (%s,%s) and joinState is %s [PASSED]!! "
+ " Found Expected: (%s)",
+ dut,
+ src_address,
+ grp_addr,
+ group_addr_json[src_address]["joinState"],
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ if not found:
+ errormsg = (
+ "[DUT %s]: Verifying iif "
+ "(Inbound Interface) for (%s, %s) "
+ "[FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ in_interface,
+ group_addr_json[src_address]["inboundInterface"],
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=12)
+def verify_join_state_and_timer(
+ tgen, dut, iif, src_address, group_addresses, expected=True
+):
+ """
+ Verify join state is updated correctly and join timer is
+ running with the help of "show ip pim upstream" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `iif`: inbound interface
+ * `src_address`: source address
+ * `group_addresses`: IGMP group address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ iif = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_join_state_and_timer(tgen, dut, iif, group_address)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+ errormsg = ""
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info(
+ "[DUT: %s]: Verifying Join state and Join Timer" " for IGMP groups received:",
+ dut,
+ )
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp in group_addresses:
+ addr_type = validate_ip_address(grp)
+
+ if addr_type == "ipv4":
+ cmd = "show ip pim upstream json"
+ elif addr_type == "ipv6":
+ cmd = "show ipv6 pim upstream json"
+ show_ip_pim_upstream_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ for grp_addr in group_addresses:
+ # Verify group address
+ if grp_addr not in show_ip_pim_upstream_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
+ dut,
+ grp_addr,
+ )
+ return errormsg
+
+ group_addr_json = show_ip_pim_upstream_json[grp_addr]
+
+ # Verify source address
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+
+ # Verify join state
+ joinState = group_addr_json[src_address]["joinState"]
+ if joinState != "Joined":
+ error = (
+ "[DUT %s]: Verifying join state for"
+ " (%s,%s) [FAILED]!! "
+ " Expected: %s, Found: %s"
+ % (dut, src_address, grp_addr, "Joined", joinState)
+ )
+ errormsg = errormsg + "\n" + str(error)
+ else:
+ logger.info(
+ "[DUT %s]: Verifying join state for"
+ " (%s,%s) [PASSED]!! "
+ " Found Expected: %s",
+ dut,
+ src_address,
+ grp_addr,
+ joinState,
+ )
+
+ # Verify join timer
+ joinTimer = group_addr_json[src_address]["joinTimer"]
+ if not re.match(r"(\d{2}):(\d{2}):(\d{2})", joinTimer):
+ error = (
+ "[DUT %s]: Verifying join timer for"
+ " (%s,%s) [FAILED]!! "
+ " Expected: %s, Found: %s"
+ ) % (
+ dut,
+ src_address,
+ grp_addr,
+ "join timer should be running",
+ joinTimer,
+ )
+ errormsg = errormsg + "\n" + str(error)
+ else:
+ logger.info(
+ "[DUT %s]: Verifying join timer is running"
+ " for (%s,%s) [PASSED]!! "
+ " Found Expected: %s",
+ dut,
+ src_address,
+ grp_addr,
+ joinTimer,
+ )
+
+ if errormsg != "":
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=120, diag_pct=0)
+def verify_mroutes(
+ tgen,
+ dut,
+ src_address,
+ group_addresses,
+ iif,
+ oil,
+ return_uptime=False,
+ mwait=0,
+ expected=True,
+):
+ """
+ Verify ip mroutes and make sure (*, G)/(S, G) is present in mroutes
+ by running "show ip/ipv6 mroute" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `src_address`: source address
+ * `group_addresses`: IGMP group address
+ * `iif`: Incoming interface
+ * `oil`: Outgoing interface
+ * `return_uptime`: If True, return uptime dict, default is False
+ * `mwait`: Wait time, default is 0
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ group_address = "225.1.1.1"
+ result = verify_mroutes(tgen, dut, src_address, group_address)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ if not isinstance(group_addresses, list):
+ group_addresses = [group_addresses]
+
+ if not isinstance(iif, list) and iif != "none":
+ iif = [iif]
+
+ if not isinstance(oil, list) and oil != "none":
+ oil = [oil]
+
+ for grp in group_addresses:
+ addr_type = validate_ip_address(grp)
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ if return_uptime:
+ logger.info("Sleeping for %s sec..", mwait)
+ sleep(mwait)
+
+ logger.info("[DUT: %s]: Verifying ip mroutes", dut)
+ show_ip_mroute_json = run_frr_cmd(
+ rnode, "show {} mroute json".format(ip_cmd), isjson=True
+ )
+
+ if return_uptime:
+ uptime_dict = {}
+
+ if bool(show_ip_mroute_json) == False:
+ error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
+ return error_msg
+
+ for grp_addr in group_addresses:
+ if grp_addr not in show_ip_mroute_json:
+ errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+ else:
+ if return_uptime:
+ uptime_dict[grp_addr] = {}
+
+ group_addr_json = show_ip_mroute_json[grp_addr]
+
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+ else:
+ if return_uptime:
+ uptime_dict[grp_addr][src_address] = {}
+
+ mroutes = group_addr_json[src_address]
+
+ if mroutes["installed"] != 0:
+ logger.info(
+ "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr
+ )
+
+ if "oil" not in mroutes:
+ if oil == "none" and mroutes["iif"] in iif:
+ logger.info(
+ "[DUT %s]: Verifying (%s, %s) mroute,"
+ " [PASSED]!! Found Expected: "
+ "(iif: %s, oil: %s, installed: (%s,%s))",
+ dut,
+ src_address,
+ grp_addr,
+ mroutes["iif"],
+ oil,
+ src_address,
+ grp_addr,
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying (%s, %s) mroute,"
+ " [FAILED]!! "
+ "Expected: (oil: %s, installed:"
+ " (%s,%s)) Found: ( oil: none, "
+ "installed: (%s,%s))"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ oil,
+ src_address,
+ grp_addr,
+ src_address,
+ grp_addr,
+ )
+ )
+
+ return errormsg
+
+ else:
+ found = False
+ for route, data in mroutes["oil"].items():
+ if route in oil and route != "pimreg":
+ if (
+ data["source"] == src_address
+ and data["group"] == grp_addr
+ and data["inboundInterface"] in iif
+ and data["outboundInterface"] in oil
+ ):
+ if return_uptime:
+
+ uptime_dict[grp_addr][src_address] = data["upTime"]
+
+ logger.info(
+ "[DUT %s]: Verifying (%s, %s)"
+ " mroute, [PASSED]!! "
+ "Found Expected: "
+ "(iif: %s, oil: %s, installed:"
+ " (%s,%s)",
+ dut,
+ src_address,
+ grp_addr,
+ data["inboundInterface"],
+ data["outboundInterface"],
+ data["source"],
+ data["group"],
+ )
+ found = True
+ break
+ else:
+ continue
+
+ if not found:
+ errormsg = (
+ "[DUT %s]: Verifying (%s, %s)"
+ " mroute [FAILED]!! "
+ "Expected in: (iif: %s, oil: %s,"
+ " installed: (%s,%s)) Found: "
+ "(iif: %s, oil: %s, "
+ "installed: (%s,%s))"
+ % (
+ dut,
+ src_address,
+ grp_addr,
+ iif,
+ oil,
+ src_address,
+ grp_addr,
+ data["inboundInterface"],
+ data["outboundInterface"],
+ data["source"],
+ data["group"],
+ )
+ )
+ return errormsg
+
+ else:
+ errormsg = "[DUT %s]: mroute (%s,%s) is not installed" % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True if return_uptime == False else uptime_dict
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_rp_info(
+ tgen,
+ topo,
+ dut,
+ group_addresses,
+ oif=None,
+ rp=None,
+ source=None,
+ iamrp=None,
+ expected=True,
+):
+ """
+ Verify pim rp info by running "show ip pim rp-info" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: JSON file handler
+ * `dut`: device under test
+ * `group_addresses`: IGMP group address
+ * `oif`: outbound interface name
+ * `rp`: RP address
+ * `source`: Source of RP
+ * `iamrp`: User defined RP
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ result = verify_pim_rp_info(tgen, topo, dut, group_address,
+ rp=rp, source="BSR")
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if type(oif) is not list:
+ oif = [oif]
+
+ for grp in group_addresses:
+ addr_type = validate_ip_address(grp)
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ for grp_addr in group_addresses:
+ if rp is None:
+ rp_details = find_rp_details(tgen, topo)
+
+ if dut in rp_details:
+ iamRP = True
+ else:
+ iamRP = False
+ else:
+ if addr_type == "ipv4":
+ show_ip_route_json = run_frr_cmd(
+ rnode, "show ip route connected json", isjson=True
+ )
+ elif addr_type == "ipv6":
+ show_ip_route_json = run_frr_cmd(
+ rnode, "show ipv6 route connected json", isjson=True
+ )
+ for _rp in show_ip_route_json.keys():
+ if rp == _rp.split("/")[0]:
+ iamRP = True
+ break
+ else:
+ iamRP = False
+
+ logger.info("[DUT: %s]: Verifying ip rp info", dut)
+ cmd = "show {} pim rp-info json".format(ip_cmd)
+ show_ip_rp_info_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ if rp not in show_ip_rp_info_json:
+ errormsg = (
+ "[DUT %s]: Verifying rp-info "
+ "for rp_address %s [FAILED]!! " % (dut, rp)
+ )
+ return errormsg
+ else:
+ group_addr_json = show_ip_rp_info_json[rp]
+
+ for rp_json in group_addr_json:
+ if "rpAddress" not in rp_json:
+ errormsg = "[DUT %s]: %s key not " "present in rp-info " % (
+ dut,
+ "rpAddress",
+ )
+ return errormsg
+
+ if oif is not None:
+ found = False
+ if rp_json["outboundInterface"] not in oif:
+ errormsg = (
+ "[DUT %s]: Verifying OIF "
+ "for group %s and RP %s [FAILED]!! "
+ "Expected interfaces: (%s),"
+ " Found: (%s)"
+ % (dut, grp_addr, rp, oif, rp_json["outboundInterface"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying OIF "
+ "for group %s and RP %s [PASSED]!! "
+ "Found Expected: (%s)"
+ % (dut, grp_addr, rp, rp_json["outboundInterface"])
+ )
+
+ if source is not None:
+ if rp_json["source"] != source:
+ errormsg = (
+ "[DUT %s]: Verifying SOURCE "
+ "for group %s and RP %s [FAILED]!! "
+ "Expected: (%s),"
+ " Found: (%s)" % (dut, grp_addr, rp, source, rp_json["source"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying SOURCE "
+ "for group %s and RP %s [PASSED]!! "
+ "Found Expected: (%s)" % (dut, grp_addr, rp, rp_json["source"])
+ )
+
+ if rp_json["group"] == grp_addr and iamrp is not None:
+ if iamRP:
+ if rp_json["iAmRP"]:
+ logger.info(
+ "[DUT %s]: Verifying group "
+ "and iAmRP [PASSED]!!"
+ " Found Expected: (%s, %s:%s)",
+ dut,
+ grp_addr,
+ "iAmRP",
+ rp_json["iAmRP"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying group"
+ "%s and iAmRP [FAILED]!! "
+ "Expected: (iAmRP: %s),"
+ " Found: (iAmRP: %s)"
+ % (dut, grp_addr, "true", rp_json["iAmRP"])
+ )
+ return errormsg
+
+ if not iamRP:
+ if rp_json["iAmRP"] == False:
+ logger.info(
+ "[DUT %s]: Verifying group "
+ "and iAmNotRP [PASSED]!!"
+ " Found Expected: (%s, %s:%s)",
+ dut,
+ grp_addr,
+ "iAmRP",
+ rp_json["iAmRP"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying group"
+ "%s and iAmRP [FAILED]!! "
+ "Expected: (iAmRP: %s),"
+ " Found: (iAmRP: %s)"
+ % (dut, grp_addr, "false", rp_json["iAmRP"])
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_state(
+ tgen,
+ dut,
+ iif,
+ oil,
+ group_addresses,
+ src_address=None,
+ installed_fl=None,
+ expected=True,
+):
+ """
+ Verify pim state by running "show ip pim state" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `iif`: inbound interface
+ * `oil`: outbound interface
+ * `group_addresses`: IGMP group address
+ * `src_address`: source address, default = None
+ * installed_fl` : Installed flag
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ iif = "r1-r3-eth1"
+ oil = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_pim_state(tgen, dut, iif, oil, group_address)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim state", dut)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp in group_addresses:
+ addr_type = validate_ip_address(grp)
+
+ if addr_type == "ipv4":
+ ip_cmd = "ip"
+ elif addr_type == "ipv6":
+ ip_cmd = "ipv6"
+
+ logger.info("[DUT: %s]: Verifying pim state", dut)
+ show_pim_state_json = run_frr_cmd(
+ rnode, "show {} pim state json".format(ip_cmd), isjson=True
+ )
+
+ if installed_fl is None:
+ installed_fl = 1
+
+ for grp_addr in group_addresses:
+ if src_address is None:
+ src_address = "*"
+ pim_state_json = show_pim_state_json[grp_addr][src_address]
+ else:
+ pim_state_json = show_pim_state_json[grp_addr][src_address]
+
+ if pim_state_json["Installed"] == installed_fl:
+ logger.info(
+ "[DUT %s]: group %s is installed flag: %s",
+ dut,
+ grp_addr,
+ pim_state_json["Installed"],
+ )
+ for interface, data in pim_state_json[iif].items():
+ if interface != oil:
+ continue
+
+ # Verify iif, oil and installed state
+ if (
+ data["group"] == grp_addr
+ and data["installed"] == installed_fl
+ and data["inboundInterface"] == iif
+ and data["outboundInterface"] == oil
+ ):
+ logger.info(
+ "[DUT %s]: Verifying pim state for group"
+ " %s [PASSED]!! Found Expected: "
+ "(iif: %s, oil: %s, installed: %s) ",
+ dut,
+ grp_addr,
+ data["inboundInterface"],
+ data["outboundInterface"],
+ data["installed"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying pim state for group"
+ " %s, [FAILED]!! Expected: "
+ "(iif: %s, oil: %s, installed: %s) "
+ % (dut, grp_addr, iif, oil, "1"),
+ "Found: (iif: %s, oil: %s, installed: %s)"
+ % (
+ data["inboundInterface"],
+ data["outboundInterface"],
+ data["installed"],
+ ),
+ )
+ return errormsg
+ else:
+ errormsg = "[DUT %s]: %s install flag value not as expected" % (
+ dut,
+ grp_addr,
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def get_pim_interface_traffic(tgen, input_dict):
+ """
+ get ip pim interface traffice by running
+ "show ip pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict(dict)`: defines DUT, what and from which interfaces
+ traffic needs to be retrieved
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "r1-r0-eth0": {
+ "helloRx": 0,
+ "helloTx": 1,
+ "joinRx": 0,
+ "joinTx": 0
+ }
+ }
+ }
+
+ result = get_pim_interface_traffic(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ output_dict = {}
+ for dut in input_dict.keys():
+ if dut not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim interface traffic", dut)
+
+ def show_pim_intf_traffic(rnode, dut, input_dict, output_dict):
+ show_pim_intf_traffic_json = run_frr_cmd(
+ rnode, "show ip pim interface traffic json", isjson=True
+ )
+
+ output_dict[dut] = {}
+ for intf, data in input_dict[dut].items():
+ interface_json = show_pim_intf_traffic_json[intf]
+ for state in data:
+
+ # Verify Tx/Rx
+ if state in interface_json:
+ output_dict[dut][state] = interface_json[state]
+ else:
+ errormsg = (
+ "[DUT %s]: %s is not present"
+ "for interface %s [FAILED]!! " % (dut, state, intf)
+ )
+ return errormsg
+ return None
+
+ test_func = functools.partial(
+ show_pim_intf_traffic, rnode, dut, input_dict, output_dict
+ )
+ (result, out) = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ if not result:
+ return out
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return output_dict
+
+
+@retry(retry_timeout=40, diag_pct=0)
+def verify_pim_interface(
+ tgen, topo, dut, interface=None, interface_ip=None, expected=True
+):
+ """
+ Verify all PIM interface are up and running, config is verified
+ using "show ip pim interface" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `interface` : interface name
+ * `interface_ip` : interface ip address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_pim_interfacetgen, topo, dut, interface=ens192, interface_ip=20.1.1.1)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in tgen.routers():
+ if router != dut:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
+
+ rnode = tgen.routers()[dut]
+ show_ip_pim_interface_json = rnode.vtysh_cmd(
+ "show ip pim interface json", isjson=True
+ )
+
+ logger.info("show_ip_pim_interface_json: \n %s", show_ip_pim_interface_json)
+
+ if interface_ip:
+ if interface in show_ip_pim_interface_json:
+ pim_intf_json = show_ip_pim_interface_json[interface]
+ if pim_intf_json["address"] != interface_ip:
+ errormsg = (
+ "[DUT %s]: PIM interface "
+ "ip is not correct "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (dut, pim_intf_json["address"], interface_ip)
+ )
+ return errormsg
+ else:
+ logger.info(
+ "[DUT %s]: PIM interface "
+ "ip is correct "
+ "[Passed]!! Expected : %s, Found : %s"
+ % (dut, pim_intf_json["address"], interface_ip)
+ )
+ return True
+ else:
+ for destLink, data in topo["routers"][dut]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ pim_interface = data["interface"]
+ pim_intf_ip = data["ipv4"].split("/")[0]
+
+ if pim_interface in show_ip_pim_interface_json:
+ pim_intf_json = show_ip_pim_interface_json[pim_interface]
+ else:
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ "PIM interface ip: %s, not Found"
+ % (dut, pim_interface, pim_intf_ip)
+ )
+ return errormsg
+
+ # Verifying PIM interface
+ if (
+ pim_intf_json["address"] != pim_intf_ip
+ and pim_intf_json["state"] != "up"
+ ):
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ "PIM interface ip: %s, status check "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (
+ dut,
+ pim_interface,
+ pim_intf_ip,
+ pim_interface,
+ pim_intf_json["state"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM interface: %s, "
+ "interface ip: %s, status: %s"
+ " [PASSED]!!",
+ dut,
+ pim_interface,
+ pim_intf_ip,
+ pim_intf_json["state"],
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def clear_pim_interface_traffic(tgen, topo):
+ """
+ Clear ip/ipv6 pim interface traffice by running
+ "clear ip/ipv6 pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ Usage
+ -----
+
+ result = clear_pim_interface_traffic(tgen, topo)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for dut in tgen.routers():
+ if "pim" not in topo["routers"][dut]:
+ continue
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Clearing pim interface traffic", dut)
+ result = run_frr_cmd(rnode, "clear ip pim interface traffic")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_pim_interfaces(tgen, dut):
+ """
+ Clear ip/ipv6 pim interface by running
+ "clear ip/ipv6 pim interfaces" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: Device Under Test
+ Usage
+ -----
+
+ result = clear_pim_interfaces(tgen, dut)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ nh_before_clear = {}
+ nh_after_clear = {}
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verify pim neighbor before pim" " neighbor clear", dut)
+ # To add uptime initially
+ sleep(10)
+ run_json_before = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
+
+ for key, value in run_json_before.items():
+ if bool(value):
+ for _key, _value in value.items():
+ nh_before_clear[key] = _value["upTime"]
+
+ # Clearing PIM neighbors
+ logger.info("[DUT: %s]: Clearing pim interfaces", dut)
+ run_frr_cmd(rnode, "clear ip pim interfaces")
+
+ logger.info("[DUT: %s]: Verify pim neighbor after pim" " neighbor clear", dut)
+
+ found = False
+
+ # Waiting for maximum 60 sec
+ fail_intf = []
+ for retry in range(1, 13):
+ logger.info("[DUT: %s]: Waiting for 5 sec for PIM neighbors" " to come up", dut)
+ sleep(5)
+ run_json_after = run_frr_cmd(rnode, "show ip pim neighbor json", isjson=True)
+ found = True
+ for pim_intf in nh_before_clear.keys():
+ if pim_intf not in run_json_after or not run_json_after[pim_intf]:
+ found = False
+ fail_intf.append(pim_intf)
+
+ if found is True:
+ break
+ else:
+ errormsg = (
+ "[DUT: %s]: pim neighborship is not formed for %s"
+ "after clear_ip_pim_interfaces %s [FAILED!!]",
+ dut,
+ fail_intf,
+ )
+ return errormsg
+
+ for key, value in run_json_after.items():
+ if bool(value):
+ for _key, _value in value.items():
+ nh_after_clear[key] = _value["upTime"]
+
+ # Verify uptime for neighbors
+ for pim_intf in nh_before_clear.keys():
+ d1 = datetime.datetime.strptime(nh_before_clear[pim_intf], "%H:%M:%S")
+ d2 = datetime.datetime.strptime(nh_after_clear[pim_intf], "%H:%M:%S")
+ if d2 >= d1:
+ errormsg = (
+ "[DUT: %s]: PIM neighborship is not cleared for",
+ " interface %s [FAILED!!]",
+ dut,
+ pim_intf,
+ )
+
+ logger.info("[DUT: %s]: PIM neighborship is cleared [PASSED!!]")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_igmp_interfaces(tgen, dut):
+ """
+ Clear ip/ipv6 igmp interfaces by running
+ "clear ip/ipv6 igmp interfaces" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+
+ Usage
+ -----
+ dut = "r1"
+ result = clear_igmp_interfaces(tgen, dut)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ group_before_clear = {}
+ group_after_clear = {}
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: IGMP group uptime before clear" " igmp groups:", dut)
+ igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
+
+ total_groups_before_clear = igmp_json["totalGroups"]
+
+ for key, value in igmp_json.items():
+ if type(value) is not dict:
+ continue
+
+ groups = value["groups"]
+ group = groups[0]["group"]
+ uptime = groups[0]["uptime"]
+ group_before_clear[group] = uptime
+
+ logger.info("[DUT: %s]: Clearing ip igmp interfaces", dut)
+ result = run_frr_cmd(rnode, "clear ip igmp interfaces")
+
+ # Waiting for maximum 60 sec
+ for retry in range(1, 13):
+ logger.info(
+ "[DUT: %s]: Waiting for 5 sec for igmp interfaces" " to come up", dut
+ )
+ sleep(5)
+ igmp_json = run_frr_cmd(rnode, "show ip igmp groups json", isjson=True)
+
+ total_groups_after_clear = igmp_json["totalGroups"]
+
+ if total_groups_before_clear == total_groups_after_clear:
+ break
+
+ for key, value in igmp_json.items():
+ if type(value) is not dict:
+ continue
+
+ groups = value["groups"]
+ group = groups[0]["group"]
+ uptime = groups[0]["uptime"]
+ group_after_clear[group] = uptime
+
+ # Verify uptime for groups
+ for group in group_before_clear.keys():
+ d1 = datetime.datetime.strptime(group_before_clear[group], "%H:%M:%S")
+ d2 = datetime.datetime.strptime(group_after_clear[group], "%H:%M:%S")
+ if d2 >= d1:
+ errormsg = ("[DUT: %s]: IGMP group is not cleared", " [FAILED!!]", dut)
+
+ logger.info("[DUT: %s]: IGMP group is cleared [PASSED!!]")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+@retry(retry_timeout=20)
+def clear_mroute_verify(tgen, dut, expected=True):
+ """
+ Clear ip/ipv6 mroute by running "clear ip/ipv6 mroute" cli and verify
+ mroutes are up again after mroute clear
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: Device Under Test
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+
+ result = clear_mroute_verify(tgen, dut)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ mroute_before_clear = {}
+ mroute_after_clear = {}
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: IP mroutes uptime before clear", dut)
+ mroute_json_1 = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
+
+ for group in mroute_json_1.keys():
+ mroute_before_clear[group] = {}
+ for key in mroute_json_1[group].keys():
+ for _key, _value in mroute_json_1[group][key]["oil"].items():
+ if _key != "pimreg":
+ mroute_before_clear[group][key] = _value["upTime"]
+
+ logger.info("[DUT: %s]: Clearing ip mroute", dut)
+ result = run_frr_cmd(rnode, "clear ip mroute")
+
+ # RFC 3376: 8.2. Query Interval - Default: 125 seconds
+ # So waiting for maximum 130 sec to get the igmp report
+ for retry in range(1, 26):
+ logger.info("[DUT: %s]: Waiting for 2 sec for mroutes" " to come up", dut)
+ sleep(5)
+ keys_json1 = mroute_json_1.keys()
+ mroute_json_2 = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
+
+ if bool(mroute_json_2):
+ keys_json2 = mroute_json_2.keys()
+
+ for group in mroute_json_2.keys():
+ flag = False
+ for key in mroute_json_2[group].keys():
+ if "oil" not in mroute_json_2[group]:
+ continue
+
+ for _key, _value in mroute_json_2[group][key]["oil"].items():
+ if _key != "pimreg" and keys_json1 == keys_json2:
+ break
+ flag = True
+ if flag:
+ break
+ else:
+ continue
+
+ for group in mroute_json_2.keys():
+ mroute_after_clear[group] = {}
+ for key in mroute_json_2[group].keys():
+ for _key, _value in mroute_json_2[group][key]["oil"].items():
+ if _key != "pimreg":
+ mroute_after_clear[group][key] = _value["upTime"]
+
+ # Verify uptime for mroute
+ for group in mroute_before_clear.keys():
+ for source in mroute_before_clear[group].keys():
+ if set(mroute_before_clear[group]) != set(mroute_after_clear[group]):
+ errormsg = (
+ "[DUT: %s]: mroute (%s, %s) has not come"
+ " up after mroute clear [FAILED!!]" % (dut, source, group)
+ )
+ return errormsg
+
+ d1 = datetime.datetime.strptime(
+ mroute_before_clear[group][source], "%H:%M:%S"
+ )
+ d2 = datetime.datetime.strptime(
+ mroute_after_clear[group][source], "%H:%M:%S"
+ )
+ if d2 >= d1:
+ errormsg = "[DUT: %s]: IP mroute is not cleared" " [FAILED!!]" % (dut)
+
+ logger.info("[DUT: %s]: IP mroute is cleared [PASSED!!]", dut)
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return True
+
+
+def clear_mroute(tgen, dut=None):
+ """
+ Clear ip/ipv6 mroute by running "clear ip mroute" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test, default None
+
+ Usage
+ -----
+ clear_mroute(tgen, dut)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ router_list = tgen.routers()
+ for router, rnode in router_list.items():
+ if dut is not None and router != dut:
+ continue
+
+ logger.debug("[DUT: %s]: Clearing ip mroute", router)
+ rnode.vtysh_cmd("clear ip mroute")
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+
+def reconfig_interfaces(tgen, topo, senderRouter, receiverRouter, packet=None):
+ """
+ Configure interface ip for sender and receiver routers
+ as per bsr packet
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `senderRouter` : Sender router
+ * `receiverRouter` : Receiver router
+ * `packet` : BSR packet in raw format
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+
+ src_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["src_ip"]
+ dest_ip = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["dest_ip"]
+
+ for destLink, data in topo["routers"][senderRouter]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ sender_interface = data["interface"]
+ sender_interface_ip = data["ipv4"]
+
+ config_data.append("interface {}".format(sender_interface))
+ config_data.append("no ip address {}".format(sender_interface_ip))
+ config_data.append("ip address {}".format(src_ip))
+
+ result = create_common_configuration(
+ tgen, senderRouter, config_data, "interface_config"
+ )
+ if result is not True:
+ return False
+
+ config_data = []
+ links = topo["routers"][destLink]["links"]
+ pim_neighbor = {key: links[key] for key in [senderRouter]}
+
+ data = pim_neighbor[senderRouter]
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ receiver_interface = data["interface"]
+ receiver_interface_ip = data["ipv4"]
+
+ config_data.append("interface {}".format(receiver_interface))
+ config_data.append("no ip address {}".format(receiver_interface_ip))
+ config_data.append("ip address {}".format(dest_ip))
+
+ result = create_common_configuration(
+ tgen, receiverRouter, config_data, "interface_config"
+ )
+ if result is not True:
+ return False
+
+ except InvalidCLIError:
+ # Traceback
+ errormsg = traceback.format_exc()
+ logger.error(errormsg)
+ return errormsg
+
+ logger.debug("Exiting lib API: reconfig_interfaces()")
+ return result
+
+
+def add_rp_interfaces_and_pim_config(tgen, topo, interface, rp, rp_mapping):
+ """
+ Add physical interfaces tp RP for all the RPs
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `interface` : RP interface
+ * `rp` : rp for given topology
+ * `rp_mapping` : dictionary of all groups and RPs
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+
+ for group, rp_list in rp_mapping.items():
+ for _rp in rp_list:
+ config_data.append("interface {}".format(interface))
+ config_data.append("ip address {}".format(_rp))
+ config_data.append("ip pim")
+
+ # Why not config just once, why per group?
+ result = create_common_configuration(
+ tgen, rp, config_data, "interface_config"
+ )
+ if result is not True:
+ return False
+
+ 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 result
+
+
+def scapy_send_bsr_raw_packet(tgen, topo, senderRouter, receiverRouter, packet=None):
+ """
+ Using scapy Raw() method to send BSR raw packet from one FRR
+ to other
+
+ Parameters:
+ -----------
+ * `tgen` : Topogen object
+ * `topo` : json file data
+ * `senderRouter` : Sender router
+ * `receiverRouter` : Receiver router
+ * `packet` : BSR packet in raw format
+
+ returns:
+ --------
+ errormsg or True
+ """
+
+ global CWD
+ result = ""
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ python3_path = tgen.net.get_exec_path(["python3", "python"])
+ script_path = os.path.join(CWD, "send_bsr_packet.py")
+ node = tgen.net[senderRouter]
+
+ for destLink, data in topo["routers"][senderRouter]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" in data and data["pim"] == "enable":
+ sender_interface = data["interface"]
+
+ packet = topo["routers"][senderRouter]["bsm"]["bsr_packets"][packet]["data"]
+
+ cmd = [
+ python3_path,
+ script_path,
+ packet,
+ sender_interface,
+ "--interval=1",
+ "--count=1",
+ ]
+ logger.info("Scapy cmd: \n %s", cmd)
+ node.cmd_raises(cmd)
+
+ logger.debug("Exiting lib API: scapy_send_bsr_raw_packet")
+ return True
+
+
+def find_rp_from_bsrp_info(tgen, dut, bsr, grp=None):
+ """
+ Find which RP is having lowest prioriy and returns rp IP
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `bsr`: BSR address
+ * 'grp': Group Address
+
+ Usage
+ -----
+ dut = "r1"
+ result = verify_pim_rp_info(tgen, dut, bsr)
+
+ Returns:
+ dictionary: group and RP, which has to be installed as per
+ lowest priority or highest priority
+ """
+
+ rp_details = {}
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Fetching rp details from bsrp-info", dut)
+ bsrp_json = run_frr_cmd(rnode, "show ip pim bsrp-info json", isjson=True)
+
+ if grp not in bsrp_json:
+ return {}
+
+ for group, rp_data in bsrp_json.items():
+ if group == "BSR Address" and bsrp_json["BSR Address"] == bsr:
+ continue
+
+ if group != grp:
+ continue
+
+ rp_priority = {}
+ rp_hash = {}
+
+ for rp, value in rp_data.items():
+ if rp == "Pending RP count":
+ continue
+ rp_priority[value["Rp Address"]] = value["Rp Priority"]
+ rp_hash[value["Rp Address"]] = value["Hash Val"]
+
+ priority_dict = dict(zip(rp_priority.values(), rp_priority.keys()))
+ hash_dict = dict(zip(rp_hash.values(), rp_hash.keys()))
+
+ # RP with lowest priority
+ if len(priority_dict) != 1:
+ rp_p, lowest_priority = sorted(rp_priority.items(), key=lambda x: x[1])[0]
+ rp_details[group] = rp_p
+
+ # RP with highest hash value
+ if len(priority_dict) == 1:
+ rp_h, highest_hash = sorted(rp_hash.items(), key=lambda x: x[1])[-1]
+ rp_details[group] = rp_h
+
+ # RP with highest IP address
+ if len(priority_dict) == 1 and len(hash_dict) == 1:
+ rp_details[group] = sorted(rp_priority.keys())[-1]
+
+ return rp_details
+
+
+@retry(retry_timeout=12)
+def verify_pim_grp_rp_source(
+ tgen, topo, dut, grp_addr, rp_source, rpadd=None, expected=True
+):
+ """
+ Verify pim rp info by running "show ip pim rp-info" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: JSON file handler
+ * `dut`: device under test
+ * `grp_addr`: IGMP group address
+ * 'rp_source': source from which rp installed
+ * 'rpadd': rp address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ group_address = "225.1.1.1"
+ rp_source = "BSR"
+ result = verify_pim_rp_and_source(tgen, topo, dut, group_address, rp_source)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying ip rp info", dut)
+ show_ip_rp_info_json = run_frr_cmd(rnode, "show ip pim rp-info json", isjson=True)
+
+ if rpadd != None:
+ rp_json = show_ip_rp_info_json[rpadd]
+ if rp_json[0]["group"] == grp_addr:
+ if rp_json[0]["source"] == rp_source:
+ logger.info(
+ "[DUT %s]: Verifying Group and rp_source [PASSED]"
+ "Found Expected: %s, %s"
+ % (dut, rp_json[0]["group"], rp_json[0]["source"])
+ )
+ return True
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying Group and rp_source [FAILED]"
+ "Expected (%s, %s) "
+ "Found (%s, %s)"
+ % (
+ dut,
+ grp_addr,
+ rp_source,
+ rp_json[0]["group"],
+ rp_json[0]["source"],
+ )
+ )
+ return errormsg
+ errormsg = (
+ "[DUT %s]: Verifying Group and rp_source [FAILED]"
+ "Expected: %s, %s but not found" % (dut, grp_addr, rp_source)
+ )
+ return errormsg
+
+ for rp in show_ip_rp_info_json:
+ rp_json = show_ip_rp_info_json[rp]
+ logger.info("%s", rp_json)
+ if rp_json[0]["group"] == grp_addr:
+ if rp_json[0]["source"] == rp_source:
+ logger.info(
+ "[DUT %s]: Verifying Group and rp_source [PASSED]"
+ "Found Expected: %s, %s"
+ % (dut, rp_json[0]["group"], rp_json[0]["source"])
+ )
+ return True
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying Group and rp_source [FAILED]"
+ "Expected (%s, %s) "
+ "Found (%s, %s)"
+ % (
+ dut,
+ grp_addr,
+ rp_source,
+ rp_json[0]["group"],
+ rp_json[0]["source"],
+ )
+ )
+ return errormsg
+
+ errormsg = (
+ "[DUT %s]: Verifying Group and rp_source [FAILED]"
+ "Expected: %s, %s but not found" % (dut, grp_addr, rp_source)
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+
+ return errormsg
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_bsr(tgen, topo, dut, bsr_ip, expected=True):
+ """
+ Verify all PIM interface are up and running, config is verified
+ using "show ip pim interface" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * 'bsr' : bsr ip to be verified
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_pim_bsr(tgen, topo, dut, bsr_ip)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in tgen.routers():
+ if router != dut:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM bsr status:", dut)
+
+ rnode = tgen.routers()[dut]
+ pim_bsr_json = rnode.vtysh_cmd("show ip pim bsr json", isjson=True)
+
+ logger.info("show_ip_pim_bsr_json: \n %s", pim_bsr_json)
+
+ # Verifying PIM bsr
+ if pim_bsr_json["bsr"] != bsr_ip:
+ errormsg = (
+ "[DUT %s]:"
+ "bsr status: not found"
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (dut, bsr_ip, pim_bsr_json["bsr"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]:" " bsr status: found, Address :%s" " [PASSED]!!",
+ dut,
+ pim_bsr_json["bsr"],
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_upstream_rpf(
+ tgen, topo, dut, interface, group_addresses, rp=None, expected=True
+):
+ """
+ Verify IP/IPv6 PIM upstream rpf, config is verified
+ using "show ip/ipv6 pim neighbor" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : devuce under test
+ * `interface` : upstream interface
+ * `group_addresses` : list of group address for which upstream info
+ needs to be checked
+ * `rp` : RP address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_pim_upstream_rpf(gen, topo, dut, interface,
+ group_addresses, rp=None)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if "pim" in topo["routers"][dut]:
+
+ logger.info("[DUT: %s]: Verifying ip pim upstream rpf:", dut)
+
+ rnode = tgen.routers()[dut]
+ show_ip_pim_upstream_rpf_json = rnode.vtysh_cmd(
+ "show ip pim upstream-rpf json", isjson=True
+ )
+
+ logger.info(
+ "show_ip_pim_upstream_rpf_json: \n %s", show_ip_pim_upstream_rpf_json
+ )
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp_addr in group_addresses:
+ for destLink, data in topo["routers"][dut]["links"].items():
+ if "type" in data and data["type"] == "loopback":
+ continue
+
+ if "pim" not in topo["routers"][destLink]:
+ continue
+
+ # Verify RP info
+ if rp is None:
+ rp_details = find_rp_details(tgen, topo)
+ else:
+ rp_details = {dut: rp}
+
+ if dut in rp_details:
+ pim_nh_intf_ip = topo["routers"][dut]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0]
+ else:
+ if destLink not in interface:
+ continue
+
+ links = topo["routers"][destLink]["links"]
+ pim_neighbor = {key: links[key] for key in [dut]}
+
+ data = pim_neighbor[dut]
+ if "pim" in data and data["pim"] == "enable":
+ pim_nh_intf_ip = data["ipv4"].split("/")[0]
+
+ upstream_rpf_json = show_ip_pim_upstream_rpf_json[grp_addr]["*"]
+
+ # Verifying ip pim upstream rpf
+ if (
+ upstream_rpf_json["rpfInterface"] == interface
+ and upstream_rpf_json["ribNexthop"] != pim_nh_intf_ip
+ ):
+ errormsg = (
+ "[DUT %s]: Verifying group: %s, "
+ "rpf interface: %s, "
+ " rib Nexthop check [FAILED]!!"
+ "Expected: %s, Found: %s"
+ % (
+ dut,
+ grp_addr,
+ interface,
+ pim_nh_intf_ip,
+ upstream_rpf_json["ribNexthop"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying group: %s,"
+ " rpf interface: %s, "
+ " rib Nexthop: %s [PASSED]!!",
+ dut,
+ grp_addr,
+ interface,
+ pim_nh_intf_ip,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def enable_disable_pim_unicast_bsm(tgen, router, intf, enable=True):
+ """
+ Helper API to enable or disable pim bsm on interfaces
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `router` : router id to be configured.
+ * `intf` : Interface to be configured
+ * `enable` : this flag denotes if config should be enabled or disabled
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+ cmd = "interface {}".format(intf)
+ config_data.append(cmd)
+
+ if enable == True:
+ config_data.append("ip pim unicast-bsm")
+ else:
+ config_data.append("no ip pim unicast-bsm")
+
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=False
+ )
+ if result is not True:
+ return False
+
+ 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 result
+
+
+def enable_disable_pim_bsm(tgen, router, intf, enable=True):
+ """
+ Helper API to enable or disable pim bsm on interfaces
+
+ Parameters
+ ----------
+ * `tgen` : Topogen object
+ * `router` : router id to be configured.
+ * `intf` : Interface to be configured
+ * `enable` : this flag denotes if config should be enabled or disabled
+
+ Returns
+ -------
+ True or False
+ """
+ result = False
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ try:
+ config_data = []
+ cmd = "interface {}".format(intf)
+ config_data.append(cmd)
+
+ if enable is True:
+ config_data.append("ip pim bsm")
+ else:
+ config_data.append("no ip pim bsm")
+
+ result = create_common_configuration(
+ tgen, router, config_data, "interface_config", build=False
+ )
+ if result is not True:
+ return False
+
+ 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 result
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_join(
+ tgen, topo, dut, interface, group_addresses, src_address=None, expected=True
+):
+ """
+ Verify ip/ipv6 pim join by running "show ip/ipv6 pim join" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo`: JSON file handler
+ * `dut`: device under test
+ * `interface`: interface name, from which PIM join would come
+ * `group_addresses`: IGMP group address
+ * `src_address`: Source address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_pim_join(tgen, dut, star, group_address, interface)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim join", dut)
+ show_pim_join_json = run_frr_cmd(rnode, "show ip pim join json", isjson=True)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp_addr in group_addresses:
+ # Verify if IGMP is enabled in DUT
+ if "igmp" not in topo["routers"][dut]:
+ pim_join = True
+ else:
+ pim_join = False
+
+ interface_json = show_pim_join_json[interface]
+
+ grp_addr = grp_addr.split("/")[0]
+ for source, data in interface_json[grp_addr].items():
+
+ # Verify pim join
+ if pim_join:
+ if data["group"] == grp_addr and data["channelJoinName"] == "JOIN":
+ logger.info(
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[PASSED]!! Found Expected: (%s)",
+ dut,
+ grp_addr,
+ data["channelJoinName"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[FAILED]!! Expected: (%s) "
+ "Found: (%s)" % (dut, grp_addr, "JOIN", data["channelJoinName"])
+ )
+ return errormsg
+
+ if not pim_join:
+ if data["group"] == grp_addr and data["channelJoinName"] == "NOINFO":
+ logger.info(
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[PASSED]!! Found Expected: (%s)",
+ dut,
+ grp_addr,
+ data["channelJoinName"],
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Verifying pim join for group: %s"
+ "[FAILED]!! Expected: (%s) "
+ "Found: (%s)"
+ % (dut, grp_addr, "NOINFO", data["channelJoinName"])
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_igmp_config(tgen, input_dict, stats_return=False, expected=True):
+ """
+ Verify igmp interface details, verifying following configs:
+ timerQueryInterval
+ timerQueryResponseIntervalMsec
+ lastMemberQueryCount
+ timerLastMemberQueryMsec
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict` : Input dict data, required to verify
+ timer
+ * `stats_return`: If user wants API to return statistics
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict ={
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "query": {
+ "query-interval" : 200,
+ "query-max-response-time" : 100
+ },
+ "statistics": {
+ "queryV2" : 2,
+ "reportV2" : 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = verify_igmp_config(tgen, input_dict, stats_return)
+
+ 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 interface, data in input_dict[dut]["igmp"]["interfaces"].items():
+
+ statistics = False
+ report = False
+ if "statistics" in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"]:
+ statistics = True
+ cmd = "show ip igmp statistics"
+ else:
+ cmd = "show ip igmp"
+
+ logger.info(
+ "[DUT: %s]: Verifying IGMP interface %s detail:", dut, interface
+ )
+
+ if statistics:
+ if (
+ "report"
+ in input_dict[dut]["igmp"]["interfaces"][interface]["igmp"][
+ "statistics"
+ ]
+ ):
+ report = True
+
+ if statistics and report:
+ show_ip_igmp_intf_json = run_frr_cmd(
+ rnode, "{} json".format(cmd), isjson=True
+ )
+ intf_detail_json = show_ip_igmp_intf_json["global"]
+ else:
+ show_ip_igmp_intf_json = run_frr_cmd(
+ rnode, "{} interface {} json".format(cmd, interface), isjson=True
+ )
+
+ if not report:
+ if interface not in show_ip_igmp_intf_json:
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ " is not present in CLI output "
+ "[FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ else:
+ intf_detail_json = show_ip_igmp_intf_json[interface]
+
+ if stats_return:
+ igmp_stats = {}
+
+ if "statistics" in data["igmp"]:
+ if stats_return:
+ igmp_stats["statistics"] = {}
+ for query, value in data["igmp"]["statistics"].items():
+ if query == "queryV2":
+ # Verifying IGMP interface queryV2 statistics
+ if stats_return:
+ igmp_stats["statistics"][query] = intf_detail_json[
+ "queryV2"
+ ]
+
+ else:
+ if intf_detail_json["queryV2"] != value:
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ " queryV2 statistics verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ intf_detail_json["queryV2"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s "
+ "queryV2 statistics is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if query == "reportV2":
+ # Verifying IGMP interface timerV2 statistics
+ if stats_return:
+ igmp_stats["statistics"][query] = intf_detail_json[
+ "reportV2"
+ ]
+
+ else:
+ if intf_detail_json["reportV2"] <= value:
+ errormsg = (
+ "[DUT %s]: IGMP reportV2 "
+ "statistics verification "
+ "[FAILED]!! Expected : %s "
+ "or more, Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP reportV2 " "statistics is %s",
+ dut,
+ intf_detail_json["reportV2"],
+ )
+
+ if "query" in data["igmp"]:
+ for query, value in data["igmp"]["query"].items():
+ if query == "query-interval":
+ # Verifying IGMP interface query interval timer
+ if intf_detail_json["timerQueryInterval"] != value:
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ " query-interval verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ intf_detail_json["timerQueryInterval"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s " "query-interval is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if query == "query-max-response-time":
+ # Verifying IGMP interface query max response timer
+ if (
+ intf_detail_json["timerQueryResponseIntervalMsec"]
+ != value * 100
+ ):
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ "query-max-response-time "
+ "verification [FAILED]!!"
+ " Expected : %s, Found : %s"
+ % (
+ dut,
+ interface,
+ value * 1000,
+ intf_detail_json["timerQueryResponseIntervalMsec"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s "
+ "query-max-response-time is %s ms",
+ dut,
+ interface,
+ value * 100,
+ )
+
+ if query == "last-member-query-count":
+ # Verifying IGMP interface last member query count
+ if intf_detail_json["lastMemberQueryCount"] != value:
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ "last-member-query-count "
+ "verification [FAILED]!!"
+ " Expected : %s, Found : %s"
+ % (
+ dut,
+ interface,
+ value,
+ intf_detail_json["lastMemberQueryCount"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s "
+ "last-member-query-count is %s ms",
+ dut,
+ interface,
+ value * 1000,
+ )
+
+ if query == "last-member-query-interval":
+ # Verifying IGMP interface last member query interval
+ if (
+ intf_detail_json["timerLastMemberQueryMsec"]
+ != value * 100 * intf_detail_json["lastMemberQueryCount"]
+ ):
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ "last-member-query-interval "
+ "verification [FAILED]!!"
+ " Expected : %s, Found : %s"
+ % (
+ dut,
+ interface,
+ value * 1000,
+ intf_detail_json["timerLastMemberQueryMsec"],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s "
+ "last-member-query-interval is %s ms",
+ dut,
+ interface,
+ value * intf_detail_json["lastMemberQueryCount"] * 100,
+ )
+
+ if "version" in data["igmp"]:
+ # Verifying IGMP interface state is up
+ if intf_detail_json["state"] != "up":
+ errormsg = (
+ "[DUT %s]: IGMP interface: %s "
+ " state: %s verification "
+ "[FAILED]!!" % (dut, interface, intf_detail_json["state"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: IGMP interface: %s " "state: %s",
+ dut,
+ interface,
+ intf_detail_json["state"],
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True if stats_return == False else igmp_stats
+
+
+@retry(retry_timeout=60, diag_pct=0)
+def verify_pim_config(tgen, input_dict, expected=True):
+ """
+ Verify pim interface details, verifying following configs:
+ drPriority
+ helloPeriod
+ helloReceived
+ helloSend
+ drAddress
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict` : Input dict data, required to verify
+ timer
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict ={
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "pim": {
+ "drPriority" : 10,
+ "helloPeriod" : 5
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = verify_pim_config(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 interface, data in input_dict[dut]["pim"]["interfaces"].items():
+
+ logger.info("[DUT: %s]: Verifying PIM interface %s detail:", dut, interface)
+
+ show_ip_igmp_intf_json = run_frr_cmd(
+ rnode, "show ip pim interface {} json".format(interface), isjson=True
+ )
+
+ if interface not in show_ip_igmp_intf_json:
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ " is not present in CLI output "
+ "[FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ intf_detail_json = show_ip_igmp_intf_json[interface]
+
+ for config, value in data.items():
+ if config == "helloPeriod":
+ # Verifying PIM interface helloPeriod
+ if intf_detail_json["helloPeriod"] != value:
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ " helloPeriod verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["helloPeriod"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM interface: %s " "helloPeriod is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if config == "drPriority":
+ # Verifying PIM interface drPriority
+ if intf_detail_json["drPriority"] != value:
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ " drPriority verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["drPriority"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM interface: %s " "drPriority is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ if config == "drAddress":
+ # Verifying PIM interface drAddress
+ if intf_detail_json["drAddress"] != value:
+ errormsg = (
+ "[DUT %s]: PIM interface: %s "
+ " drAddress verification "
+ "[FAILED]!! Expected : %s,"
+ " Found : %s"
+ % (dut, interface, value, intf_detail_json["drAddress"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: PIM interface: %s " "drAddress is %s",
+ dut,
+ interface,
+ value,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=20, diag_pct=0)
+def verify_multicast_traffic(tgen, input_dict, return_traffic=False, expected=True):
+ """
+ Verify multicast traffic by running
+ "show multicast traffic count json" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict(dict)`: defines DUT, what and for which interfaces
+ traffic needs to be verified
+ * `return_traffic`: returns traffic stats
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "traffic_received": ["r1-r0-eth0"],
+ "traffic_sent": ["r1-r0-eth0"]
+ }
+ }
+
+ result = verify_multicast_traffic(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ traffic_dict = {}
+ for dut in input_dict.keys():
+ if dut not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying multicast " "traffic", dut)
+
+ show_multicast_traffic_json = run_frr_cmd(
+ rnode, "show ip multicast count json", isjson=True
+ )
+
+ for traffic_type, interfaces in input_dict[dut].items():
+ traffic_dict[traffic_type] = {}
+ if traffic_type == "traffic_received":
+ for interface in interfaces:
+ traffic_dict[traffic_type][interface] = {}
+ interface_json = show_multicast_traffic_json[interface]
+
+ if interface_json["pktsIn"] == 0 and interface_json["bytesIn"] == 0:
+ errormsg = (
+ "[DUT %s]: Multicast traffic is "
+ "not received on interface %s "
+ "PktsIn: %s, BytesIn: %s "
+ "[FAILED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsIn"],
+ interface_json["bytesIn"],
+ )
+ )
+ return errormsg
+
+ elif (
+ interface_json["pktsIn"] != 0 and interface_json["bytesIn"] != 0
+ ):
+
+ traffic_dict[traffic_type][interface][
+ "pktsIn"
+ ] = interface_json["pktsIn"]
+ traffic_dict[traffic_type][interface][
+ "bytesIn"
+ ] = interface_json["bytesIn"]
+
+ logger.info(
+ "[DUT %s]: Multicast traffic is "
+ "received on interface %s "
+ "PktsIn: %s, BytesIn: %s "
+ "[PASSED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsIn"],
+ interface_json["bytesIn"],
+ )
+ )
+
+ else:
+ errormsg = (
+ "[DUT %s]: Multicast traffic interface %s:"
+ " Miss-match in "
+ "PktsIn: %s, BytesIn: %s"
+ "[FAILED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsIn"],
+ interface_json["bytesIn"],
+ )
+ )
+ return errormsg
+
+ if traffic_type == "traffic_sent":
+ traffic_dict[traffic_type] = {}
+ for interface in interfaces:
+ traffic_dict[traffic_type][interface] = {}
+ interface_json = show_multicast_traffic_json[interface]
+
+ if (
+ interface_json["pktsOut"] == 0
+ and interface_json["bytesOut"] == 0
+ ):
+ errormsg = (
+ "[DUT %s]: Multicast traffic is "
+ "not received on interface %s "
+ "PktsIn: %s, BytesIn: %s"
+ "[FAILED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsOut"],
+ interface_json["bytesOut"],
+ )
+ )
+ return errormsg
+
+ elif (
+ interface_json["pktsOut"] != 0
+ and interface_json["bytesOut"] != 0
+ ):
+
+ traffic_dict[traffic_type][interface][
+ "pktsOut"
+ ] = interface_json["pktsOut"]
+ traffic_dict[traffic_type][interface][
+ "bytesOut"
+ ] = interface_json["bytesOut"]
+
+ logger.info(
+ "[DUT %s]: Multicast traffic is "
+ "received on interface %s "
+ "PktsOut: %s, BytesOut: %s "
+ "[PASSED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsOut"],
+ interface_json["bytesOut"],
+ )
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: Multicast traffic interface %s:"
+ " Miss-match in "
+ "PktsOut: %s, BytesOut: %s "
+ "[FAILED]!!"
+ % (
+ dut,
+ interface,
+ interface_json["pktsOut"],
+ interface_json["bytesOut"],
+ )
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True if return_traffic == False else traffic_dict
+
+
+def get_refCount_for_mroute(tgen, dut, iif, src_address, group_addresses):
+ """
+ Verify upstream inbound interface is updated correctly
+ by running "show ip pim upstream" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `iif`: inbound interface
+ * `src_address`: source address
+ * `group_addresses`: IGMP group address
+
+ Usage
+ -----
+ dut = "r1"
+ iif = "r1-r0-eth0"
+ src_address = "*"
+ group_address = "225.1.1.1"
+ result = get_refCount_for_mroute(tgen, dut, iif, src_address,
+ group_address)
+
+ Returns
+ -------
+ refCount(int)
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ refCount = 0
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying refCount for mroutes: ", dut)
+ show_ip_pim_upstream_json = run_frr_cmd(
+ rnode, "show ip pim upstream json", isjson=True
+ )
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp_addr in group_addresses:
+ # Verify group address
+ if grp_addr not in show_ip_pim_upstream_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for group %s [FAILED]!!" % (
+ dut,
+ grp_addr,
+ )
+ return errormsg
+ group_addr_json = show_ip_pim_upstream_json[grp_addr]
+
+ # Verify source address
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying upstream" " for (%s,%s) [FAILED]!!" % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+
+ # Verify Inbound Interface
+ if group_addr_json[src_address]["inboundInterface"] == iif:
+ refCount = group_addr_json[src_address]["refCount"]
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return refCount
+
+
+@retry(retry_timeout=40, diag_pct=0)
+def verify_multicast_flag_state(
+ tgen, dut, src_address, group_addresses, flag, expected=True
+):
+ """
+ Verify flag state for mroutes and make sure (*, G)/(S, G) are having
+ coorect flags by running "show ip mroute" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `src_address`: source address
+ * `group_addresses`: IGMP group address
+ * `flag`: flag state, needs to be verified
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ dut = "r1"
+ flag = "SC"
+ group_address = "225.1.1.1"
+ result = verify_multicast_flag_state(tgen, dut, src_address,
+ group_address, flag)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying flag state for mroutes", dut)
+ show_ip_mroute_json = run_frr_cmd(rnode, "show ip mroute json", isjson=True)
+
+ if bool(show_ip_mroute_json) == False:
+ error_msg = "[DUT %s]: mroutes are not present or flushed out !!" % (dut)
+ return error_msg
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ for grp_addr in group_addresses:
+ if grp_addr not in show_ip_mroute_json:
+ errormsg = (
+ "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! ",
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+ else:
+ group_addr_json = show_ip_mroute_json[grp_addr]
+
+ if src_address not in group_addr_json:
+ errormsg = "[DUT %s]: Verifying (%s, %s) mroute," "[FAILED]!! " % (
+ dut,
+ src_address,
+ grp_addr,
+ )
+ return errormsg
+ else:
+ mroutes = group_addr_json[src_address]
+
+ if mroutes["installed"] != 0:
+ logger.info(
+ "[DUT %s]: mroute (%s,%s) is installed", dut, src_address, grp_addr
+ )
+
+ if mroutes["flags"] != flag:
+ errormsg = (
+ "[DUT %s]: Verifying flag for (%s, %s) "
+ "mroute [FAILED]!! "
+ "Expected: %s Found: %s"
+ % (dut, src_address, grp_addr, flag, mroutes["flags"])
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying flag for (%s, %s)"
+ " mroute, [PASSED]!! "
+ "Found Expected: %s",
+ dut,
+ src_address,
+ grp_addr,
+ mroutes["flags"],
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+@retry(retry_timeout=40, diag_pct=0)
+def verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip, expected=True):
+ """
+ Verify all IGMP interface are up and running, config is verified
+ using "show ip igmp interface" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `topo` : json file data
+ * `dut` : device under test
+ * `igmp_iface` : interface name
+ * `interface_ip` : interface ip address
+ * `expected` : expected results from API, by-default True
+
+ Usage
+ -----
+ result = verify_igmp_interface(tgen, topo, dut, igmp_iface, interface_ip)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ for router in tgen.routers():
+ if router != dut:
+ continue
+
+ logger.info("[DUT: %s]: Verifying PIM interface status:", dut)
+
+ rnode = tgen.routers()[dut]
+ show_ip_igmp_interface_json = run_frr_cmd(
+ rnode, "show ip igmp interface json", isjson=True
+ )
+
+ if igmp_iface in show_ip_igmp_interface_json:
+ igmp_intf_json = show_ip_igmp_interface_json[igmp_iface]
+ # Verifying igmp interface
+ if igmp_intf_json["address"] != interface_ip:
+ errormsg = (
+ "[DUT %s]: igmp interface ip is not correct "
+ "[FAILED]!! Expected : %s, Found : %s"
+ % (dut, igmp_intf_json["address"], interface_ip)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: igmp interface: %s, " "interface ip: %s" " [PASSED]!!",
+ dut,
+ igmp_iface,
+ interface_ip,
+ )
+ else:
+ errormsg = (
+ "[DUT %s]: igmp interface: %s "
+ "igmp interface ip: %s, is not present "
+ % (dut, igmp_iface, interface_ip)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+class McastTesterHelper(HostApplicationHelper):
+ def __init__(self, tgen=None):
+ self.script_path = os.path.join(CWD, "mcast-tester.py")
+ self.host_conn = {}
+ self.listen_sock = None
+
+ # # Get a temporary file for socket path
+ # (fd, sock_path) = tempfile.mkstemp("-mct.sock", "tmp" + str(os.getpid()))
+ # os.close(fd)
+ # os.remove(sock_path)
+ # self.app_sock_path = sock_path
+
+ # # Listen on unix socket
+ # logger.debug("%s: listening on socket %s", self, self.app_sock_path)
+ # self.listen_sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0)
+ # self.listen_sock.settimeout(10)
+ # self.listen_sock.bind(self.app_sock_path)
+ # self.listen_sock.listen(10)
+
+ python3_path = get_exec_path(["python3", "python"])
+ super(McastTesterHelper, self).__init__(
+ tgen,
+ # [python3_path, self.script_path, self.app_sock_path]
+ [python3_path, self.script_path],
+ )
+
+ def __str__(self):
+ return "McastTesterHelper({})".format(self.script_path)
+
+ def run_join(self, host, join_addrs, join_towards=None, join_intf=None):
+ """
+ Join a UDP multicast group.
+
+ One of join_towards or join_intf MUST be set.
+
+ Parameters:
+ -----------
+ * `host`: host from where IGMP join would be sent
+ * `join_addrs`: multicast address (or addresses) to join to
+ * `join_intf`: the interface to bind the join[s] to
+ * `join_towards`: router whos interface to bind the join[s] to
+ """
+ if not isinstance(join_addrs, list) and not isinstance(join_addrs, tuple):
+ join_addrs = [join_addrs]
+
+ if join_towards:
+ join_intf = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][join_towards]["interface"]
+ )
+ else:
+ assert join_intf
+
+ for join in join_addrs:
+ self.run(host, [join, join_intf])
+
+ return True
+
+ def run_traffic(self, host, send_to_addrs, bind_towards=None, bind_intf=None):
+ """
+ Send UDP multicast traffic.
+
+ One of bind_towards or bind_intf MUST be set.
+
+ Parameters:
+ -----------
+ * `host`: host to send traffic from
+ * `send_to_addrs`: multicast address (or addresses) to send traffic to
+ * `bind_towards`: Router who's interface the source ip address is got from
+ """
+ if bind_towards:
+ bind_intf = frr_unicode(
+ self.tgen.json_topo["routers"][host]["links"][bind_towards]["interface"]
+ )
+ else:
+ assert bind_intf
+
+ if not isinstance(send_to_addrs, list) and not isinstance(send_to_addrs, tuple):
+ send_to_addrs = [send_to_addrs]
+
+ for send_to in send_to_addrs:
+ self.run(host, ["--send=0.7", send_to, bind_intf])
+
+ return True
+
+
+@retry(retry_timeout=62)
+def verify_local_igmp_groups(tgen, dut, interface, group_addresses):
+ """
+ Verify local IGMP groups are received from an intended interface
+ by running "show ip igmp join json" command
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `dut`: device under test
+ * `interface`: interface, from which IGMP groups are configured
+ * `group_addresses`: IGMP group address
+
+ Usage
+ -----
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ group_address = "225.1.1.1"
+ result = verify_local_igmp_groups(tgen, dut, interface, group_address)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ if dut not in tgen.routers():
+ return False
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying local IGMP groups received:", dut)
+ show_ip_local_igmp_json = run_frr_cmd(rnode, "show ip igmp join json", isjson=True)
+
+ if type(group_addresses) is not list:
+ group_addresses = [group_addresses]
+
+ if interface not in show_ip_local_igmp_json:
+
+ errormsg = (
+ "[DUT %s]: Verifying local IGMP group received"
+ " from interface %s [FAILED]!! " % (dut, interface)
+ )
+ return errormsg
+
+ for grp_addr in group_addresses:
+ found = False
+ for index in show_ip_local_igmp_json[interface]["groups"]:
+ if index["group"] == grp_addr:
+ found = True
+ break
+ if not found:
+ errormsg = (
+ "[DUT %s]: Verifying local IGMP group received"
+ " from interface %s [FAILED]!! "
+ " Expected: %s " % (dut, interface, grp_addr)
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT %s]: Verifying local IGMP group %s received "
+ "from interface %s [PASSED]!! ",
+ dut,
+ grp_addr,
+ interface,
+ )
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True
+
+
+def verify_pim_interface_traffic(tgen, input_dict, return_stats=True, addr_type="ipv4"):
+ """
+ Verify ip pim interface traffice by running
+ "show ip pim interface traffic" cli
+
+ Parameters
+ ----------
+ * `tgen`: topogen object
+ * `input_dict(dict)`: defines DUT, what and from which interfaces
+ traffic needs to be verified
+ * [optional]`addr_type`: specify address-family, default is ipv4
+
+ Usage
+ -----
+ input_dict = {
+ "r1": {
+ "r1-r0-eth0": {
+ "helloRx": 0,
+ "helloTx": 1,
+ "joinRx": 0,
+ "joinTx": 0
+ }
+ }
+ }
+
+ result = verify_pim_interface_traffic(tgen, input_dict)
+
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ logger.debug("Entering lib API: {}".format(sys._getframe().f_code.co_name))
+
+ output_dict = {}
+ for dut in input_dict.keys():
+ if dut not in tgen.routers():
+ continue
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: Verifying pim interface traffic", dut)
+
+ if addr_type == "ipv4":
+ cmd = "show ip pim interface traffic json"
+ elif addr_type == "ipv6":
+ cmd = "show ipv6 pim interface traffic json"
+
+ show_pim_intf_traffic_json = run_frr_cmd(rnode, cmd, isjson=True)
+
+ output_dict[dut] = {}
+ for intf, data in input_dict[dut].items():
+ interface_json = show_pim_intf_traffic_json[intf]
+ for state in data:
+
+ # Verify Tx/Rx
+ if state in interface_json:
+ output_dict[dut][state] = interface_json[state]
+ else:
+ errormsg = (
+ "[DUT %s]: %s is not present"
+ "for interface %s [FAILED]!! " % (dut, state, intf)
+ )
+ return errormsg
+
+ logger.debug("Exiting lib API: {}".format(sys._getframe().f_code.co_name))
+ return True if return_stats == False else output_dict
+
+ # def cleanup(self):
+ # super(McastTesterHelper, self).cleanup()
+
+ # if not self.listen_sock:
+ # return
+
+ # logger.debug("%s: closing listen socket %s", self, self.app_sock_path)
+ # self.listen_sock.close()
+ # self.listen_sock = None
+
+ # if os.path.exists(self.app_sock_path):
+ # os.remove(self.app_sock_path)
+
+ # def started_proc(self, host, p):
+ # logger.debug("%s: %s: accepting on socket %s", self, host, self.app_sock_path)
+ # try:
+ # conn = self.listen_sock.accept()
+ # return conn
+ # except Exception as error:
+ # logger.error("%s: %s: accept on socket failed: %s", self, host, error)
+ # if p.poll() is not None:
+ # logger.error("%s: %s: helper app quit: %s", self, host, comm_error(p))
+ # raise
+
+ # def stopping_proc(self, host, p, conn):
+ # logger.debug("%s: %s: closing socket %s", self, host, conn)
+ # conn[0].close()
diff --git a/tests/topotests/lib/scapy_sendpkt.py b/tests/topotests/lib/scapy_sendpkt.py
new file mode 100755
index 0000000..0bb6a72
--- /dev/null
+++ b/tests/topotests/lib/scapy_sendpkt.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# July 29 2021, Christian Hopps <chopps@labn.net>
+#
+# Copyright (c) 2021, LabN Consulting, L.L.C. ("LabN")
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+#
+import argparse
+import logging
+import re
+import sys
+
+from scapy.all import conf, srp
+
+conf.verb = 0
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("-i", "--interface", help="interface to send packet on.")
+ parser.add_argument("-I", "--imports", help="scapy symbols to import")
+ parser.add_argument(
+ "-t", "--timeout", type=float, default=2.0, help="timeout for reply receipts"
+ )
+ parser.add_argument("pktdef", help="scapy packet definition to send")
+ args = parser.parse_args()
+
+ if args.imports:
+ i = args.imports.replace("\n", "").strip()
+ if not re.match("[a-zA-Z0-9_ \t,]", i):
+ logging.critical('Invalid imports specified: "%s"', i)
+ sys.exit(1)
+ exec("from scapy.all import " + i, globals(), locals())
+
+ ans, unans = srp(eval(args.pktdef), iface=args.interface, timeout=args.timeout)
+ if not ans:
+ sys.exit(2)
+ for pkt in ans:
+ print(pkt.answer.show(dump=True))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/tests/topotests/lib/send_bsr_packet.py b/tests/topotests/lib/send_bsr_packet.py
new file mode 100755
index 0000000..c226899
--- /dev/null
+++ b/tests/topotests/lib/send_bsr_packet.py
@@ -0,0 +1,58 @@
+#
+# Copyright (c) 2019 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.
+#
+
+import sys
+import argparse
+from scapy.all import Raw, sendp
+import binascii
+
+
+def send_packet(packet, iface, interval, count):
+ """
+ Read BSR packet in Raw format and send it to specified interface
+
+ Parameter:
+ ---------
+ * `packet` : BSR packet in raw format
+ * `interface` : Interface from which packet would be send
+ * `interval` : Interval between the packets
+ * `count` : Number of packets to be sent
+ """
+
+ data = binascii.a2b_hex(packet)
+ p = Raw(load=data)
+ p.show()
+ sendp(p, inter=int(interval), iface=iface, count=int(count))
+
+
+if __name__ == "__main__":
+ parser = argparse.ArgumentParser(description="Send BSR Raw packet")
+ parser.add_argument("packet", help="Packet in raw format")
+ parser.add_argument("iface", help="Packet send to this ineterface")
+ parser.add_argument("--interval", help="Interval between packets", default=0)
+ parser.add_argument(
+ "--count", help="Number of times packet is sent repetitively", default=0
+ )
+ args = parser.parse_args()
+
+ if not args.packet or not args.iface:
+ sys.exit(1)
+
+ send_packet(args.packet, args.iface, args.interval, args.count)
diff --git a/tests/topotests/lib/snmptest.py b/tests/topotests/lib/snmptest.py
new file mode 100644
index 0000000..fe5ff28
--- /dev/null
+++ b/tests/topotests/lib/snmptest.py
@@ -0,0 +1,155 @@
+#
+# topogen.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+#
+# 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.
+#
+
+"""
+SNMP library to test snmp walks and gets
+
+Basic usage instructions:
+
+* define an SnmpTester class giving a router, address, community and version
+* use test_oid or test_walk to check values in MIBS
+* see tests/topotest/simple-snmp-test/test_simple_snmp.py for example
+"""
+
+from lib.topolog import logger
+
+
+class SnmpTester(object):
+ "A helper class for testing SNMP"
+
+ def __init__(self, router, iface, community, version):
+ self.community = community
+ self.version = version
+ self.router = router
+ self.iface = iface
+ logger.info(
+ "created SNMP tester: SNMPv{0} community:{1}".format(
+ self.version, self.community
+ )
+ )
+
+ def _snmp_config(self):
+ """
+ Helper function to build a string with SNMP
+ configuration for commands.
+ """
+ return "-v {0} -c {1} {2}".format(self.version, self.community, self.iface)
+
+ @staticmethod
+ def _get_snmp_value(snmp_output):
+ tokens = snmp_output.strip().split()
+
+ num_value_tokens = len(tokens) - 3
+
+ # this copes with the emptys string return
+ if num_value_tokens == 0:
+ return tokens[2]
+
+ if num_value_tokens > 1:
+ output = ""
+ index = 3
+ while index < len(tokens) - 1:
+ output += "{} ".format(tokens[index])
+ index += 1
+ output += "{}".format(tokens[index])
+ return output
+ # third token is the value of the object
+ return tokens[3]
+
+ @staticmethod
+ def _get_snmp_oid(snmp_output):
+ tokens = snmp_output.strip().split()
+
+ # third token onwards is the value of the object
+ return tokens[0].split(".", 1)[1]
+
+ @staticmethod
+ def _get_snmp_oid(snmp_output):
+ tokens = snmp_output.strip().split()
+
+ # if len(tokens) > 5:
+ # return None
+
+ # third token is the value of the object
+ return tokens[0].split(".", 1)[1]
+
+ def _parse_multiline(self, snmp_output):
+ results = snmp_output.strip().split("\n")
+
+ out_dict = {}
+ out_list = []
+ for response in results:
+ out_dict[self._get_snmp_oid(response)] = self._get_snmp_value(response)
+ out_list.append(self._get_snmp_value(response))
+
+ return out_dict, out_list
+
+ def get(self, oid):
+ cmd = "snmpget {0} {1}".format(self._snmp_config(), oid)
+
+ result = self.router.cmd(cmd)
+ if "not found" in result:
+ return None
+ return self._get_snmp_value(result)
+
+ def get_next(self, oid):
+ cmd = "snmpgetnext {0} {1}".format(self._snmp_config(), oid)
+
+ result = self.router.cmd(cmd)
+ print("get_next: {}".format(result))
+ if "not found" in result:
+ return None
+ return self._get_snmp_value(result)
+
+ def walk(self, oid):
+ cmd = "snmpwalk {0} {1}".format(self._snmp_config(), oid)
+
+ result = self.router.cmd(cmd)
+ return self._parse_multiline(result)
+
+ def test_oid(self, oid, value):
+ print("oid: {}".format(self.get_next(oid)))
+ return self.get_next(oid) == value
+
+ def test_oid_walk(self, oid, values, oids=None):
+ results_dict, results_list = self.walk(oid)
+ print("test_oid_walk: {} {}".format(oid, results_dict))
+ if oids is not None:
+ index = 0
+ for oid in oids:
+ # avoid key error for missing keys
+ if not oid in results_dict.keys():
+ print("FAIL: missing oid key {}".format(oid))
+ return False
+ if results_dict[oid] != values[index]:
+ print(
+ "FAIL{} {} |{}| == |{}|".format(
+ oid, index, results_dict[oid], values[index]
+ )
+ )
+ return False
+ index += 1
+ return True
+
+ # Return true if 'values' is a subset of 'results_list'
+ print("test {} == {}".format(results_list[: len(values)], values))
+ return results_list[: len(values)] == values
diff --git a/tests/topotests/lib/test/__init__.py b/tests/topotests/lib/test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/lib/test/__init__.py
diff --git a/tests/topotests/lib/test/test_json.py b/tests/topotests/lib/test/test_json.py
new file mode 100755
index 0000000..7b3c859
--- /dev/null
+++ b/tests/topotests/lib/test/test_json.py
@@ -0,0 +1,641 @@
+#!/usr/bin/env python
+
+#
+# test_json.py
+# Tests for library function: json_cmp().
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+Tests for the json_cmp() function.
+"""
+
+import os
+import sys
+import pytest
+
+# Save the Current Working Directory to find lib files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+from lib.topotest import json_cmp
+
+
+def test_json_intersect_true():
+ "Test simple correct JSON intersections"
+
+ dcomplete = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": "item3",
+ "i100": "item4",
+ }
+
+ dsub1 = {
+ "i1": "item1",
+ "i3": "item3",
+ }
+ dsub2 = {
+ "i1": "item1",
+ "i2": "item2",
+ }
+ dsub3 = {
+ "i100": "item4",
+ "i2": "item2",
+ }
+ dsub4 = {
+ "i50": None,
+ "i100": "item4",
+ }
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+ assert json_cmp(dcomplete, dsub4) is None
+
+
+def test_json_intersect_false():
+ "Test simple incorrect JSON intersections"
+
+ dcomplete = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": "item3",
+ "i100": "item4",
+ }
+
+ # Incorrect value for 'i1'
+ dsub1 = {
+ "i1": "item3",
+ "i3": "item3",
+ }
+ # Non-existing key 'i5'
+ dsub2 = {
+ "i1": "item1",
+ "i5": "item2",
+ }
+ # Key should not exist
+ dsub3 = {
+ "i100": None,
+ }
+
+ assert json_cmp(dcomplete, dsub1) is not None
+ assert json_cmp(dcomplete, dsub2) is not None
+ assert json_cmp(dcomplete, dsub3) is not None
+
+
+def test_json_intersect_multilevel_true():
+ "Test multi level correct JSON intersections"
+
+ dcomplete = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": {
+ "i100": "item100",
+ },
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item421",
+ "i422": "item422",
+ },
+ },
+ }
+
+ dsub1 = {
+ "i1": "item1",
+ "i3": {
+ "i100": "item100",
+ },
+ "i10": None,
+ }
+ dsub2 = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": {},
+ }
+ dsub3 = {
+ "i2": "item2",
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i422": "item422",
+ "i450": None,
+ },
+ },
+ }
+ dsub4 = {
+ "i2": "item2",
+ "i4": {
+ "i41": {},
+ "i42": {
+ "i450": None,
+ },
+ },
+ }
+ dsub5 = {
+ "i2": "item2",
+ "i3": {
+ "i100": "item100",
+ },
+ "i4": {
+ "i42": {
+ "i450": None,
+ }
+ },
+ }
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+ assert json_cmp(dcomplete, dsub4) is None
+ assert json_cmp(dcomplete, dsub5) is None
+
+
+def test_json_intersect_multilevel_false():
+ "Test multi level incorrect JSON intersections"
+
+ dcomplete = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": {
+ "i100": "item100",
+ },
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item421",
+ "i422": "item422",
+ },
+ },
+ }
+
+ # Incorrect sub-level value
+ dsub1 = {
+ "i1": "item1",
+ "i3": {
+ "i100": "item00",
+ },
+ "i10": None,
+ }
+ # Inexistent sub-level
+ dsub2 = {
+ "i1": "item1",
+ "i2": "item2",
+ "i3": None,
+ }
+ # Inexistent sub-level value
+ dsub3 = {
+ "i1": "item1",
+ "i3": {
+ "i100": None,
+ },
+ }
+ # Inexistent sub-sub-level value
+ dsub4 = {
+ "i4": {
+ "i41": {
+ "i412": "item412",
+ },
+ "i42": {
+ "i421": "item421",
+ },
+ }
+ }
+ # Invalid sub-sub-level value
+ dsub5 = {
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": {
+ "i421": "item420000",
+ },
+ }
+ }
+ # sub-sub-level should be value
+ dsub6 = {
+ "i4": {
+ "i41": {
+ "i411": "item411",
+ },
+ "i42": "foobar",
+ }
+ }
+
+ assert json_cmp(dcomplete, dsub1) is not None
+ assert json_cmp(dcomplete, dsub2) is not None
+ assert json_cmp(dcomplete, dsub3) is not None
+ assert json_cmp(dcomplete, dsub4) is not None
+ assert json_cmp(dcomplete, dsub5) is not None
+ assert json_cmp(dcomplete, dsub6) is not None
+
+
+def test_json_with_list_sucess():
+ "Test successful json comparisons that have lists."
+
+ dcomplete = {
+ "list": [
+ {
+ "i1": "item 1",
+ "i2": "item 2",
+ },
+ {
+ "i10": "item 10",
+ },
+ ],
+ "i100": "item 100",
+ }
+
+ # Test list type
+ dsub1 = {
+ "list": [],
+ }
+ # Test list correct list items
+ dsub2 = {
+ "list": [
+ {
+ "i1": "item 1",
+ },
+ ],
+ "i100": "item 100",
+ }
+ # Test list correct list size
+ dsub3 = {
+ "list": [
+ {},
+ {},
+ ],
+ }
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+
+
+def test_json_with_list_failure():
+ "Test failed json comparisons that have lists."
+
+ dcomplete = {
+ "list": [
+ {
+ "i1": "item 1",
+ "i2": "item 2",
+ },
+ {
+ "i10": "item 10",
+ },
+ ],
+ "i100": "item 100",
+ }
+
+ # Test list type
+ dsub1 = {
+ "list": {},
+ }
+ # Test list incorrect list items
+ dsub2 = {
+ "list": [
+ {
+ "i1": "item 2",
+ },
+ ],
+ "i100": "item 100",
+ }
+ # Test list correct list size
+ dsub3 = {
+ "list": [
+ {},
+ {},
+ {},
+ ],
+ }
+
+ assert json_cmp(dcomplete, dsub1) is not None
+ assert json_cmp(dcomplete, dsub2) is not None
+ assert json_cmp(dcomplete, dsub3) is not None
+
+
+def test_json_list_start_success():
+ "Test JSON encoded data that starts with a list that should succeed."
+
+ dcomplete = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abcd",
+ },
+ {
+ "id": 300,
+ "value": "abcde",
+ },
+ ]
+
+ dsub1 = [
+ {
+ "id": 100,
+ "value": "abc",
+ }
+ ]
+
+ dsub2 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abcd",
+ },
+ ]
+
+ dsub3 = [
+ {
+ "id": 300,
+ "value": "abcde",
+ }
+ ]
+
+ dsub4 = []
+
+ dsub5 = [
+ {
+ "id": 100,
+ }
+ ]
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+ assert json_cmp(dcomplete, dsub4) is None
+ assert json_cmp(dcomplete, dsub5) is None
+
+
+def test_json_list_start_failure():
+ "Test JSON encoded data that starts with a list that should fail."
+
+ dcomplete = [
+ {"id": 100, "value": "abc"},
+ {"id": 200, "value": "abcd"},
+ {"id": 300, "value": "abcde"},
+ ]
+
+ dsub1 = [
+ {
+ "id": 100,
+ "value": "abcd",
+ }
+ ]
+
+ dsub2 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 200,
+ "value": "abc",
+ },
+ ]
+
+ dsub3 = [
+ {
+ "id": 100,
+ "value": "abc",
+ },
+ {
+ "id": 350,
+ "value": "abcde",
+ },
+ ]
+
+ dsub4 = [
+ {
+ "value": "abcx",
+ },
+ {
+ "id": 300,
+ "value": "abcde",
+ },
+ ]
+
+ assert json_cmp(dcomplete, dsub1) is not None
+ assert json_cmp(dcomplete, dsub2) is not None
+ assert json_cmp(dcomplete, dsub3) is not None
+ assert json_cmp(dcomplete, dsub4) is not None
+
+
+def test_json_list_ordered():
+ "Test JSON encoded data that should be ordered using the '__ordered__' tag."
+
+ dcomplete = [
+ {"id": 1, "value": "abc"},
+ "some string",
+ 123,
+ ]
+
+ dsub1 = [
+ "__ordered__",
+ "some string",
+ {"id": 1, "value": "abc"},
+ 123,
+ ]
+
+ assert json_cmp(dcomplete, dsub1) is not None
+
+
+def test_json_list_exact_matching():
+ "Test JSON array on exact matching using the 'exact' parameter."
+
+ dcomplete = [
+ {"id": 1, "value": "abc"},
+ "some string",
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub1 = [
+ "some string",
+ {"id": 1, "value": "abc"},
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub2 = [
+ {"id": 1},
+ "some string",
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub3 = [
+ {"id": 1, "value": "abc"},
+ "some string",
+ 123,
+ [1, 3, 2],
+ ]
+
+ assert json_cmp(dcomplete, dsub1, exact=True) is not None
+ assert json_cmp(dcomplete, dsub2, exact=True) is not None
+
+
+def test_json_object_exact_matching():
+ "Test JSON object on exact matching using the 'exact' parameter."
+
+ dcomplete = {
+ "a": {"id": 1, "value": "abc"},
+ "b": "some string",
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub1 = {
+ "a": {"id": 1, "value": "abc"},
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub2 = {
+ "a": {"id": 1},
+ "b": "some string",
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub3 = {
+ "a": {"id": 1, "value": "abc"},
+ "b": "some string",
+ "c": 123,
+ "d": [1, 3],
+ }
+
+ assert json_cmp(dcomplete, dsub1, exact=True) is not None
+ assert json_cmp(dcomplete, dsub2, exact=True) is not None
+ assert json_cmp(dcomplete, dsub3, exact=True) is not None
+
+
+def test_json_list_asterisk_matching():
+ "Test JSON array elements on matching '*' as a placeholder for arbitrary data."
+
+ dcomplete = [
+ {"id": 1, "value": "abc"},
+ "some string",
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub1 = [
+ "*",
+ "some string",
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub2 = [
+ {"id": "*", "value": "abc"},
+ "some string",
+ 123,
+ [1, 2, 3],
+ ]
+
+ dsub3 = [
+ {"id": 1, "value": "abc"},
+ "some string",
+ 123,
+ [1, "*", 3],
+ ]
+
+ dsub4 = [
+ "*",
+ "some string",
+ "*",
+ [1, 2, 3],
+ ]
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+ assert json_cmp(dcomplete, dsub4) is None
+
+
+def test_json_object_asterisk_matching():
+ "Test JSON object value elements on matching '*' as a placeholder for arbitrary data."
+
+ dcomplete = {
+ "a": {"id": 1, "value": "abc"},
+ "b": "some string",
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub1 = {
+ "a": "*",
+ "b": "some string",
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub2 = {
+ "a": {"id": 1, "value": "abc"},
+ "b": "some string",
+ "c": 123,
+ "d": [1, "*", 3],
+ }
+
+ dsub3 = {
+ "a": {"id": "*", "value": "abc"},
+ "b": "some string",
+ "c": 123,
+ "d": [1, 2, 3],
+ }
+
+ dsub4 = {
+ "a": "*",
+ "b": "some string",
+ "c": "*",
+ "d": [1, 2, 3],
+ }
+
+ assert json_cmp(dcomplete, dsub1) is None
+ assert json_cmp(dcomplete, dsub2) is None
+ assert json_cmp(dcomplete, dsub3) is None
+ assert json_cmp(dcomplete, dsub4) is None
+
+
+def test_json_list_nested_with_objects():
+
+ dcomplete = [{"key": 1, "list": [123]}, {"key": 2, "list": [123]}]
+
+ dsub1 = [{"key": 2, "list": [123]}, {"key": 1, "list": [123]}]
+
+ assert json_cmp(dcomplete, dsub1) is None
+
+
+if __name__ == "__main__":
+ sys.exit(pytest.main())
diff --git a/tests/topotests/lib/test/test_run_and_expect.py b/tests/topotests/lib/test/test_run_and_expect.py
new file mode 100755
index 0000000..d65d5ba
--- /dev/null
+++ b/tests/topotests/lib/test/test_run_and_expect.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+#
+# test_run_and_expect.py
+# Tests for library function: run_and_expect(_type)().
+#
+# Copyright (c) 2019 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.
+#
+
+"""
+Tests for the `run_and_expect(_type)()` functions.
+"""
+
+import os
+import sys
+import pytest
+
+# Save the Current Working Directory to find lib files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+from lib.topotest import run_and_expect_type
+
+
+def test_run_and_expect_type():
+ "Test basic `run_and_expect_type` functionality."
+
+ def return_true():
+ "Test function that returns `True`."
+ return True
+
+ # Test value success.
+ success, value = run_and_expect_type(
+ return_true, bool, count=1, wait=0, avalue=True
+ )
+ assert success is True
+ assert value is True
+
+ # Test value failure.
+ success, value = run_and_expect_type(
+ return_true, bool, count=1, wait=0, avalue=False
+ )
+ assert success is False
+ assert value is True
+
+ # Test type success.
+ success, value = run_and_expect_type(return_true, bool, count=1, wait=0)
+ assert success is True
+ assert value is True
+
+ # Test type failure.
+ success, value = run_and_expect_type(return_true, str, count=1, wait=0)
+ assert success is False
+ assert value is True
+
+ # Test type failure, return correct type.
+ success, value = run_and_expect_type(return_true, str, count=1, wait=0, avalue=True)
+ assert success is False
+ assert value is True
+
+
+if __name__ == "__main__":
+ sys.exit(pytest.main())
diff --git a/tests/topotests/lib/test/test_version.py b/tests/topotests/lib/test/test_version.py
new file mode 100755
index 0000000..7c2df00
--- /dev/null
+++ b/tests/topotests/lib/test/test_version.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+
+#
+# test_version.py
+# Tests for library function: version_cmp().
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+Tests for the version_cmp() function.
+"""
+
+import os
+import sys
+import pytest
+
+# Save the Current Working Directory to find lib files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../../"))
+
+# pylint: disable=C0413
+from lib.topotest import version_cmp
+
+
+def test_valid_versions():
+ "Test valid version compare results"
+
+ curver = "3.0"
+ samever = "3"
+ oldver = "2.0"
+ newver = "3.0.1"
+ newerver = "3.0.11"
+ vercustom = "3.0-dev"
+ verysmallinc = "3.0.0.0.0.0.0.1"
+
+ assert version_cmp(curver, oldver) == 1
+ assert version_cmp(curver, newver) == -1
+ assert version_cmp(curver, curver) == 0
+ assert version_cmp(curver, newerver) == -1
+ assert version_cmp(newver, newerver) == -1
+ assert version_cmp(curver, samever) == 0
+ assert version_cmp(curver, vercustom) == 0
+ assert version_cmp(vercustom, vercustom) == 0
+ assert version_cmp(vercustom, oldver) == 1
+ assert version_cmp(vercustom, newver) == -1
+ assert version_cmp(vercustom, samever) == 0
+ assert version_cmp(curver, verysmallinc) == -1
+ assert version_cmp(newver, verysmallinc) == 1
+ assert version_cmp(verysmallinc, verysmallinc) == 0
+ assert version_cmp(vercustom, verysmallinc) == -1
+
+
+def test_invalid_versions():
+ "Test invalid version strings"
+
+ curver = "3.0"
+ badver1 = ".1"
+ badver2 = "-1.0"
+ badver3 = "."
+ badver4 = "3.-0.3"
+
+ with pytest.raises(ValueError):
+ assert version_cmp(curver, badver1)
+ assert version_cmp(curver, badver2)
+ assert version_cmp(curver, badver3)
+ assert version_cmp(curver, badver4)
+
+
+def test_regression_1():
+ """
+ Test regression on the following type of comparison: '3.0.2' > '3'
+ Expected result is 1.
+ """
+ assert version_cmp("3.0.2", "3") == 1
diff --git a/tests/topotests/lib/topogen.py b/tests/topotests/lib/topogen.py
new file mode 100644
index 0000000..04712ed
--- /dev/null
+++ b/tests/topotests/lib/topogen.py
@@ -0,0 +1,1338 @@
+#
+# topogen.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+Topogen (Topology Generator) is an abstraction around Topotest and Mininet to
+help reduce boilerplate code and provide a stable interface to build topology
+tests on.
+
+Basic usage instructions:
+
+* Define a Topology class with a build method using mininet.topo.Topo.
+ See examples/test_template.py.
+* Use Topogen inside the build() method with get_topogen.
+ e.g. get_topogen(self).
+* Start up your topology with: Topogen(YourTopology)
+* Initialize the Mininet with your topology with: tgen.start_topology()
+* Configure your routers/hosts and start them
+* Run your tests / mininet cli.
+* After running stop Mininet with: tgen.stop_topology()
+"""
+
+import grp
+import inspect
+import json
+import logging
+import os
+import platform
+import pwd
+import re
+import subprocess
+import sys
+from collections import OrderedDict
+
+if sys.version_info[0] > 2:
+ import configparser
+else:
+ import ConfigParser as configparser
+
+import lib.topolog as topolog
+from lib.micronet import Commander
+from lib.micronet_compat import Mininet
+from lib.topolog import logger
+from lib.topotest import g_extra_config
+
+from lib import topotest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+# pylint: disable=C0103
+# Global Topogen variable. This is being used to keep the Topogen available on
+# all test functions without declaring a test local variable.
+global_tgen = None
+
+
+def get_topogen(topo=None):
+ """
+ Helper function to retrieve Topogen. Must be called with `topo` when called
+ inside the build() method of Topology class.
+ """
+ if topo is not None:
+ global_tgen.topo = topo
+ return global_tgen
+
+
+def set_topogen(tgen):
+ "Helper function to set Topogen"
+ # pylint: disable=W0603
+ global global_tgen
+ global_tgen = tgen
+
+
+def is_string(value):
+ """Return True if value is a string."""
+ try:
+ return isinstance(value, basestring) # type: ignore
+ except NameError:
+ return isinstance(value, str)
+
+
+def get_exabgp_cmd(commander=None):
+ """Return the command to use for ExaBGP version < 4."""
+
+ if commander is None:
+ commander = Commander("topogen")
+
+ def exacmd_version_ok(exacmd):
+ logger.debug("checking %s for exabgp < version 4", exacmd)
+ _, stdout, _ = commander.cmd_status(exacmd + " -v", warn=False)
+ m = re.search(r"ExaBGP\s*:\s*((\d+)\.(\d+)(?:\.(\d+))?)", stdout)
+ if not m:
+ return False
+ version = m.group(1)
+ if topotest.version_cmp(version, "4") >= 0:
+ logging.debug("found exabgp version >= 4 in %s will keep looking", exacmd)
+ return False
+ logger.info("Using ExaBGP version %s in %s", version, exacmd)
+ return True
+
+ exacmd = commander.get_exec_path("exabgp")
+ if exacmd and exacmd_version_ok(exacmd):
+ return exacmd
+ py2_path = commander.get_exec_path("python2")
+ if py2_path:
+ exacmd = py2_path + " -m exabgp"
+ if exacmd_version_ok(exacmd):
+ return exacmd
+ py2_path = commander.get_exec_path("python")
+ if py2_path:
+ exacmd = py2_path + " -m exabgp"
+ if exacmd_version_ok(exacmd):
+ return exacmd
+ return None
+
+
+#
+# Main class: topology builder
+#
+
+# Topogen configuration defaults
+tgen_defaults = {
+ "verbosity": "info",
+ "frrdir": "/usr/lib/frr",
+ "routertype": "frr",
+ "memleak_path": "",
+}
+
+
+class Topogen(object):
+ "A topology test builder helper."
+
+ CONFIG_SECTION = "topogen"
+
+ def __init__(self, topodef, modname="unnamed"):
+ """
+ Topogen initialization function, takes the following arguments:
+ * `cls`: OLD:uthe topology class that is child of mininet.topo or a build function.
+ * `topodef`: A dictionary defining the topology, a filename of a json file, or a
+ function that will do the same
+ * `modname`: module name must be a unique name to identify logs later.
+ """
+ self.config = None
+ self.net = None
+ self.gears = {}
+ self.routern = 1
+ self.switchn = 1
+ self.modname = modname
+ self.errorsd = {}
+ self.errors = ""
+ self.peern = 1
+ self.cfg_gen = 0
+ self.exabgp_cmd = None
+ self._init_topo(topodef)
+
+ logger.info("loading topology: {}".format(self.modname))
+
+ # @staticmethod
+ # def _mininet_reset():
+ # "Reset the mininet environment"
+ # # Clean up the mininet environment
+ # os.system("mn -c > /dev/null 2>&1")
+
+ def __str__(self):
+ return "Topogen()"
+
+ def _init_topo(self, topodef):
+ """
+ Initialize the topogily provided by the user. The user topology class
+ must call get_topogen() during build() to get the topogen object.
+ """
+ # Set the global variable so the test cases can access it anywhere
+ set_topogen(self)
+
+ # Increase host based limits
+ topotest.fix_host_limits()
+
+ # Test for MPLS Kernel modules available
+ self.hasmpls = False
+ if not topotest.module_present("mpls-router"):
+ logger.info("MPLS tests will not run (missing mpls-router kernel module)")
+ elif not topotest.module_present("mpls-iptunnel"):
+ logger.info("MPLS tests will not run (missing mpls-iptunnel kernel module)")
+ else:
+ self.hasmpls = True
+
+ # Load the default topology configurations
+ self._load_config()
+
+ # Create new log directory
+ self.logdir = topotest.get_logs_path(g_extra_config["rundir"])
+ subprocess.check_call(
+ "mkdir -p {0} && chmod 1777 {0}".format(self.logdir), shell=True
+ )
+ try:
+ routertype = self.config.get(self.CONFIG_SECTION, "routertype")
+ # Only allow group, if it exist.
+ gid = grp.getgrnam(routertype)[2]
+ os.chown(self.logdir, 0, gid)
+ os.chmod(self.logdir, 0o775)
+ except KeyError:
+ # Allow anyone, but set the sticky bit to avoid file deletions
+ os.chmod(self.logdir, 0o1777)
+
+ # Remove old twisty way of creating sub-classed topology object which has it's
+ # build method invoked which calls Topogen methods which then call Topo methods
+ # to create a topology within the Topo object, which is then used by
+ # Mininet(Micronet) to build the actual topology.
+ assert not inspect.isclass(topodef)
+
+ self.net = Mininet(controller=None)
+
+ # New direct way: Either a dictionary defines the topology or a build function
+ # is supplied, or a json filename all of which build the topology by calling
+ # Topogen methods which call Mininet(Micronet) methods to create the actual
+ # topology.
+ if not inspect.isclass(topodef):
+ if callable(topodef):
+ topodef(self)
+ self.net.configure_hosts()
+ elif is_string(topodef):
+ # topojson imports topogen in one function too,
+ # switch away from this use here to the topojson
+ # fixutre and remove this case
+ from lib.topojson import build_topo_from_json
+
+ with open(topodef, "r") as topof:
+ self.json_topo = json.load(topof)
+ build_topo_from_json(self, self.json_topo)
+ self.net.configure_hosts()
+ elif topodef:
+ self.add_topology_from_dict(topodef)
+
+ def add_topology_from_dict(self, topodef):
+
+ keylist = (
+ topodef.keys()
+ if isinstance(topodef, OrderedDict)
+ else sorted(topodef.keys())
+ )
+ # ---------------------------
+ # Create all referenced hosts
+ # ---------------------------
+ for oname in keylist:
+ tup = (topodef[oname],) if is_string(topodef[oname]) else topodef[oname]
+ for e in tup:
+ desc = e.split(":")
+ name = desc[0]
+ if name not in self.gears:
+ logging.debug("Adding router: %s", name)
+ self.add_router(name)
+
+ # ------------------------------
+ # Create all referenced switches
+ # ------------------------------
+ for oname in keylist:
+ if oname is not None and oname not in self.gears:
+ logging.debug("Adding switch: %s", oname)
+ self.add_switch(oname)
+
+ # ----------------
+ # Create all links
+ # ----------------
+ for oname in keylist:
+ if oname is None:
+ continue
+ tup = (topodef[oname],) if is_string(topodef[oname]) else topodef[oname]
+ for e in tup:
+ desc = e.split(":")
+ name = desc[0]
+ ifname = desc[1] if len(desc) > 1 else None
+ sifname = desc[2] if len(desc) > 2 else None
+ self.add_link(self.gears[oname], self.gears[name], sifname, ifname)
+
+ self.net.configure_hosts()
+
+ def _load_config(self):
+ """
+ Loads the configuration file `pytest.ini` located at the root dir of
+ topotests.
+ """
+ self.config = configparser.ConfigParser(tgen_defaults)
+ pytestini_path = os.path.join(CWD, "../pytest.ini")
+ self.config.read(pytestini_path)
+
+ def add_router(self, name=None, cls=None, **params):
+ """
+ Adds a new router to the topology. This function has the following
+ options:
+ * `name`: (optional) select the router name
+ * `daemondir`: (optional) custom daemon binary directory
+ * `routertype`: (optional) `frr`
+ Returns a TopoRouter.
+ """
+ if cls is None:
+ cls = topotest.Router
+ if name is None:
+ name = "r{}".format(self.routern)
+ if name in self.gears:
+ raise KeyError("router already exists")
+
+ params["frrdir"] = self.config.get(self.CONFIG_SECTION, "frrdir")
+ params["memleak_path"] = self.config.get(self.CONFIG_SECTION, "memleak_path")
+ if "routertype" not in params:
+ params["routertype"] = self.config.get(self.CONFIG_SECTION, "routertype")
+
+ self.gears[name] = TopoRouter(self, cls, name, **params)
+ self.routern += 1
+ return self.gears[name]
+
+ def add_switch(self, name=None):
+ """
+ Adds a new switch to the topology. This function has the following
+ options:
+ name: (optional) select the switch name
+ Returns the switch name and number.
+ """
+ if name is None:
+ name = "s{}".format(self.switchn)
+ if name in self.gears:
+ raise KeyError("switch already exists")
+
+ self.gears[name] = TopoSwitch(self, name)
+ self.switchn += 1
+ return self.gears[name]
+
+ def add_exabgp_peer(self, name, ip, defaultRoute):
+ """
+ Adds a new ExaBGP peer to the topology. This function has the following
+ parameters:
+ * `ip`: the peer address (e.g. '1.2.3.4/24')
+ * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1')
+ """
+ if name is None:
+ name = "peer{}".format(self.peern)
+ if name in self.gears:
+ raise KeyError("exabgp peer already exists")
+
+ self.gears[name] = TopoExaBGP(self, name, ip=ip, defaultRoute=defaultRoute)
+ self.peern += 1
+ return self.gears[name]
+
+ def add_host(self, name, ip, defaultRoute):
+ """
+ Adds a new host to the topology. This function has the following
+ parameters:
+ * `ip`: the peer address (e.g. '1.2.3.4/24')
+ * `defaultRoute`: the peer default route (e.g. 'via 1.2.3.1')
+ """
+ if name is None:
+ name = "host{}".format(self.peern)
+ if name in self.gears:
+ raise KeyError("host already exists")
+
+ self.gears[name] = TopoHost(self, name, ip=ip, defaultRoute=defaultRoute)
+ self.peern += 1
+ return self.gears[name]
+
+ def add_link(self, node1, node2, ifname1=None, ifname2=None):
+ """
+ Creates a connection between node1 and node2. The nodes can be the
+ following:
+ * TopoGear
+ * TopoRouter
+ * TopoSwitch
+ """
+ if not isinstance(node1, TopoGear):
+ raise ValueError("invalid node1 type")
+ if not isinstance(node2, TopoGear):
+ raise ValueError("invalid node2 type")
+
+ if ifname1 is None:
+ ifname1 = node1.new_link()
+ if ifname2 is None:
+ ifname2 = node2.new_link()
+
+ node1.register_link(ifname1, node2, ifname2)
+ node2.register_link(ifname2, node1, ifname1)
+ self.net.add_link(node1.name, node2.name, ifname1, ifname2)
+
+ def get_gears(self, geartype):
+ """
+ Returns a dictionary of all gears of type `geartype`.
+
+ Normal usage:
+ * Dictionary iteration:
+ ```py
+ tgen = get_topogen()
+ router_dict = tgen.get_gears(TopoRouter)
+ for router_name, router in router_dict.items():
+ # Do stuff
+ ```
+ * List iteration:
+ ```py
+ tgen = get_topogen()
+ peer_list = tgen.get_gears(TopoExaBGP).values()
+ for peer in peer_list:
+ # Do stuff
+ ```
+ """
+ return dict(
+ (name, gear)
+ for name, gear in self.gears.items()
+ if isinstance(gear, geartype)
+ )
+
+ def routers(self):
+ """
+ Returns the router dictionary (key is the router name and value is the
+ router object itself).
+ """
+ return self.get_gears(TopoRouter)
+
+ def exabgp_peers(self):
+ """
+ Returns the exabgp peer dictionary (key is the peer name and value is
+ the peer object itself).
+ """
+ return self.get_gears(TopoExaBGP)
+
+ def start_topology(self):
+ """Starts the topology class."""
+ logger.info("starting topology: {}".format(self.modname))
+ self.net.start()
+
+ def start_router(self, router=None):
+ """
+ Call the router startRouter method.
+ If no router is specified it is called for all registered routers.
+ """
+ if router is None:
+ # pylint: disable=r1704
+ # XXX should be hosts?
+ for _, router in self.routers().items():
+ router.start()
+ else:
+ if isinstance(router, str):
+ router = self.gears[router]
+
+ router.start()
+
+ def stop_topology(self):
+ """
+ Stops the network topology. This function will call the stop() function
+ of all gears before calling the mininet stop function, so they can have
+ their oportunity to do a graceful shutdown. stop() is called twice. The
+ first is a simple kill with no sleep, the second will sleep if not
+ killed and try with a different signal.
+ """
+ logger.info("stopping topology: {}".format(self.modname))
+ errors = ""
+ for gear in self.gears.values():
+ errors += gear.stop()
+ if len(errors) > 0:
+ logger.error(
+ "Errors found post shutdown - details follow: {}".format(errors)
+ )
+
+ self.net.stop()
+
+ def get_exabgp_cmd(self):
+ if not self.exabgp_cmd:
+ self.exabgp_cmd = get_exabgp_cmd(self.net)
+ return self.exabgp_cmd
+
+ def cli(self):
+ """
+ Interrupt the test and call the command line interface for manual
+ inspection. Should be only used on non production code.
+ """
+ self.net.cli()
+
+ mininet_cli = cli
+
+ def is_memleak_enabled(self):
+ "Returns `True` if memory leak report is enable, otherwise `False`."
+ # On router failure we can't run the memory leak test
+ if self.routers_have_failure():
+ return False
+
+ memleak_file = os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.config.get(
+ self.CONFIG_SECTION, "memleak_path"
+ )
+ if memleak_file == "" or memleak_file == None:
+ return False
+ return True
+
+ def report_memory_leaks(self, testname=None):
+ "Run memory leak test and reports."
+ if not self.is_memleak_enabled():
+ return
+
+ # If no name was specified, use the test module name
+ if testname is None:
+ testname = self.modname
+
+ router_list = self.routers().values()
+ for router in router_list:
+ router.report_memory_leaks(self.modname)
+
+ def set_error(self, message, code=None):
+ "Sets an error message and signal other tests to skip."
+ logger.info(message)
+
+ # If no code is defined use a sequential number
+ if code is None:
+ code = len(self.errorsd)
+
+ self.errorsd[code] = message
+ self.errors += "\n{}: {}".format(code, message)
+
+ def has_errors(self):
+ "Returns whether errors exist or not."
+ return len(self.errorsd) > 0
+
+ def routers_have_failure(self):
+ "Runs an assertion to make sure that all routers are running."
+ if self.has_errors():
+ return True
+
+ errors = ""
+ router_list = self.routers().values()
+ for router in router_list:
+ result = router.check_router_running()
+ if result != "":
+ errors += result + "\n"
+
+ if errors != "":
+ self.set_error(errors, "router_error")
+ assert False, errors
+ return True
+ return False
+
+
+#
+# Topology gears (equipment)
+#
+
+
+class TopoGear(object):
+ "Abstract class for type checking"
+
+ def __init__(self, tgen, name, **params):
+ self.tgen = tgen
+ self.name = name
+ self.params = params
+ self.links = {}
+ self.linkn = 0
+
+ # Would be nice for this to point at the gears log directory rather than the
+ # test's.
+ self.logdir = tgen.logdir
+ self.gearlogdir = None
+
+ def __str__(self):
+ links = ""
+ for myif, dest in self.links.items():
+ _, destif = dest
+ if links != "":
+ links += ","
+ links += '"{}"<->"{}"'.format(myif, destif)
+
+ return 'TopoGear<name="{}",links=[{}]>'.format(self.name, links)
+
+ @property
+ def net(self):
+ return self.tgen.net[self.name]
+
+ def start(self):
+ "Basic start function that just reports equipment start"
+ logger.info('starting "{}"'.format(self.name))
+
+ def stop(self, wait=True, assertOnError=True):
+ "Basic stop function that just reports equipment stop"
+ logger.info('"{}" base stop called'.format(self.name))
+ return ""
+
+ def cmd(self, command, **kwargs):
+ """
+ Runs the provided command string in the router and returns a string
+ with the response.
+ """
+ return self.net.cmd_legacy(command, **kwargs)
+
+ def cmd_raises(self, command, **kwargs):
+ """
+ Runs the provided command string in the router and returns a string
+ with the response. Raise an exception on any error.
+ """
+ return self.net.cmd_raises(command, **kwargs)
+
+ run = cmd
+
+ def popen(self, *params, **kwargs):
+ """
+ Creates a pipe with the given command. Same args as python Popen.
+ If `command` is a string then will be invoked with shell, otherwise
+ `command` is a list and will be invoked w/o shell. Returns a popen object.
+ """
+ return self.net.popen(*params, **kwargs)
+
+ def add_link(self, node, myif=None, nodeif=None):
+ """
+ Creates a link (connection) between myself and the specified node.
+ Interfaces name can be speficied with:
+ myif: the interface name that will be created in this node
+ nodeif: the target interface name that will be created on the remote node.
+ """
+ self.tgen.add_link(self, node, myif, nodeif)
+
+ def link_enable(self, myif, enabled=True, netns=None):
+ """
+ Set this node interface administrative state.
+ myif: this node interface name
+ enabled: whether we should enable or disable the interface
+ """
+ if myif not in self.links.keys():
+ raise KeyError("interface doesn't exists")
+
+ if enabled is True:
+ operation = "up"
+ else:
+ operation = "down"
+
+ logger.info(
+ 'setting node "{}" link "{}" to state "{}"'.format(
+ self.name, myif, operation
+ )
+ )
+ extract = ""
+ if netns is not None:
+ extract = "ip netns exec {} ".format(netns)
+
+ return self.run("{}ip link set dev {} {}".format(extract, myif, operation))
+
+ def peer_link_enable(self, myif, enabled=True, netns=None):
+ """
+ Set the peer interface administrative state.
+ myif: this node interface name
+ enabled: whether we should enable or disable the interface
+
+ NOTE: this is used to simulate a link down on this node, since when the
+ peer disables their interface our interface status changes to no link.
+ """
+ if myif not in self.links.keys():
+ raise KeyError("interface doesn't exists")
+
+ node, nodeif = self.links[myif]
+ node.link_enable(nodeif, enabled, netns)
+
+ def new_link(self):
+ """
+ Generates a new unique link name.
+
+ NOTE: This function should only be called by Topogen.
+ """
+ ifname = "{}-eth{}".format(self.name, self.linkn)
+ self.linkn += 1
+ return ifname
+
+ def register_link(self, myif, node, nodeif):
+ """
+ Register link between this node interface and outside node.
+
+ NOTE: This function should only be called by Topogen.
+ """
+ if myif in self.links.keys():
+ raise KeyError("interface already exists")
+
+ self.links[myif] = (node, nodeif)
+
+ def _setup_tmpdir(self):
+ topotest.setup_node_tmpdir(self.logdir, self.name)
+ self.gearlogdir = "{}/{}".format(self.logdir, self.name)
+ return "{}/{}.log".format(self.logdir, self.name)
+
+
+class TopoRouter(TopoGear):
+ """
+ Router abstraction.
+ """
+
+ # The default required directories by FRR
+ PRIVATE_DIRS = [
+ "/etc/frr",
+ "/etc/snmp",
+ "/var/run/frr",
+ "/var/log",
+ ]
+
+ # Router Daemon enumeration definition.
+ RD_FRR = 0 # not a daemon, but use to setup unified configs
+ RD_ZEBRA = 1
+ RD_RIP = 2
+ RD_RIPNG = 3
+ RD_OSPF = 4
+ RD_OSPF6 = 5
+ RD_ISIS = 6
+ RD_BGP = 7
+ RD_LDP = 8
+ RD_PIM = 9
+ RD_EIGRP = 10
+ RD_NHRP = 11
+ RD_STATIC = 12
+ RD_BFD = 13
+ RD_SHARP = 14
+ RD_BABEL = 15
+ RD_PBRD = 16
+ RD_PATH = 17
+ RD_SNMP = 18
+ RD_PIM6 = 19
+ RD = {
+ RD_FRR: "frr",
+ RD_ZEBRA: "zebra",
+ RD_RIP: "ripd",
+ RD_RIPNG: "ripngd",
+ RD_OSPF: "ospfd",
+ RD_OSPF6: "ospf6d",
+ RD_ISIS: "isisd",
+ RD_BGP: "bgpd",
+ RD_PIM: "pimd",
+ RD_PIM6: "pim6d",
+ RD_LDP: "ldpd",
+ RD_EIGRP: "eigrpd",
+ RD_NHRP: "nhrpd",
+ RD_STATIC: "staticd",
+ RD_BFD: "bfdd",
+ RD_SHARP: "sharpd",
+ RD_BABEL: "babeld",
+ RD_PBRD: "pbrd",
+ RD_PATH: "pathd",
+ RD_SNMP: "snmpd",
+ }
+
+ def __init__(self, tgen, cls, name, **params):
+ """
+ The constructor has the following parameters:
+ * tgen: Topogen object
+ * cls: router class that will be used to instantiate
+ * name: router name
+ * daemondir: daemon binary directory
+ * routertype: 'frr'
+ """
+ super(TopoRouter, self).__init__(tgen, name, **params)
+ self.routertype = params.get("routertype", "frr")
+ if "privateDirs" not in params:
+ params["privateDirs"] = self.PRIVATE_DIRS
+
+ # Propagate the router log directory
+ logfile = self._setup_tmpdir()
+ params["logdir"] = self.logdir
+
+ self.logger = topolog.get_logger(name, log_level="debug", target=logfile)
+ params["logger"] = self.logger
+ tgen.net.add_host(self.name, cls=cls, **params)
+ topotest.fix_netns_limits(tgen.net[name])
+
+ # Mount gear log directory on a common path
+ self.net.bind_mount(self.gearlogdir, "/tmp/gearlogdir")
+
+ # Ensure pid file
+ with open(os.path.join(self.logdir, self.name + ".pid"), "w") as f:
+ f.write(str(self.net.pid) + "\n")
+
+ def __str__(self):
+ gear = super(TopoRouter, self).__str__()
+ gear += " TopoRouter<>"
+ return gear
+
+ def check_capability(self, daemon, param):
+ """
+ Checks a capability daemon against an argument option
+ Return True if capability available. False otherwise
+ """
+ daemonstr = self.RD.get(daemon)
+ self.logger.info('check capability {} for "{}"'.format(param, daemonstr))
+ return self.net.checkCapability(daemonstr, param)
+
+ def load_frr_config(self, source, daemons=None):
+ """
+ Loads the unified configuration file source
+ Start the daemons in the list
+ If daemons is None, try to infer daemons from the config file
+ """
+ self.load_config(self.RD_FRR, source)
+ if not daemons:
+ # Always add zebra
+ self.load_config(self.RD_ZEBRA)
+ for daemon in self.RD:
+ # This will not work for all daemons
+ daemonstr = self.RD.get(daemon).rstrip("d")
+ if daemonstr == "pim":
+ grep_cmd = "grep 'ip {}' {}".format(daemonstr, source)
+ else:
+ grep_cmd = "grep 'router {}' {}".format(daemonstr, source)
+ result = self.run(grep_cmd).strip()
+ if result:
+ self.load_config(daemon)
+ else:
+ for daemon in daemons:
+ self.load_config(daemon)
+
+ def load_config(self, daemon, source=None, param=None):
+ """Loads daemon configuration from the specified source
+ Possible daemon values are: TopoRouter.RD_ZEBRA, TopoRouter.RD_RIP,
+ TopoRouter.RD_RIPNG, TopoRouter.RD_OSPF, TopoRouter.RD_OSPF6,
+ TopoRouter.RD_ISIS, TopoRouter.RD_BGP, TopoRouter.RD_LDP,
+ TopoRouter.RD_PIM, TopoRouter.RD_PIM6, TopoRouter.RD_PBR,
+ TopoRouter.RD_SNMP.
+
+ Possible `source` values are `None` for an empty config file, a path name which is
+ used directly, or a file name with no path components which is first looked for
+ directly and then looked for under a sub-directory named after router.
+
+ This API unfortunately allows for source to not exist for any and
+ all routers.
+ """
+ daemonstr = self.RD.get(daemon)
+ self.logger.info('loading "{}" configuration: {}'.format(daemonstr, source))
+ self.net.loadConf(daemonstr, source, param)
+
+ def check_router_running(self):
+ """
+ Run a series of checks and returns a status string.
+ """
+ self.logger.info("checking if daemons are running")
+ return self.net.checkRouterRunning()
+
+ def start(self):
+ """
+ Start router:
+ * Load modules
+ * Clean up files
+ * Configure interfaces
+ * Start daemons (e.g. FRR)
+ * Configure daemon logging files
+ """
+
+ nrouter = self.net
+ result = nrouter.startRouter(self.tgen)
+
+ # Enable command logging
+
+ # Enable all daemon command logging, logging files
+ # and set them to the start dir.
+ for daemon, enabled in nrouter.daemons.items():
+ if enabled and daemon != "snmpd":
+ self.vtysh_cmd(
+ "\n".join(
+ [
+ "clear log cmdline-targets",
+ "conf t",
+ "log file {}.log debug".format(daemon),
+ "log commands",
+ "log timestamp precision 3",
+ ]
+ ),
+ daemon=daemon,
+ )
+
+ if result != "":
+ self.tgen.set_error(result)
+ elif nrouter.daemons["ldpd"] == 1 or nrouter.daemons["pathd"] == 1:
+ # Enable MPLS processing on all interfaces.
+ for interface in self.links:
+ topotest.sysctl_assure(
+ nrouter, "net.mpls.conf.{}.input".format(interface), 1
+ )
+
+ return result
+
+ def stop(self):
+ """
+ Stop router cleanly:
+ * Signal daemons twice, once with SIGTERM, then with SIGKILL.
+ """
+ self.logger.debug("stopping (no assert)")
+ return self.net.stopRouter(False)
+
+ def startDaemons(self, daemons):
+ """
+ Start Daemons: to start specific daemon(user defined daemon only)
+ * Start daemons (e.g. FRR)
+ * Configure daemon logging files
+ """
+ self.logger.debug("starting")
+ nrouter = self.net
+ result = nrouter.startRouterDaemons(daemons)
+
+ if daemons is None:
+ daemons = nrouter.daemons.keys()
+
+ # Enable all daemon command logging, logging files
+ # and set them to the start dir.
+ for daemon in daemons:
+ enabled = nrouter.daemons[daemon]
+ if enabled and daemon != "snmpd":
+ self.vtysh_cmd(
+ "\n".join(
+ [
+ "clear log cmdline-targets",
+ "conf t",
+ "log file {}.log debug".format(daemon),
+ "log commands",
+ "log timestamp precision 3",
+ ]
+ ),
+ daemon=daemon,
+ )
+
+ if result != "":
+ self.tgen.set_error(result)
+
+ return result
+
+ def killDaemons(self, daemons, wait=True, assertOnError=True):
+ """
+ Kill specific daemon(user defined daemon only)
+ forcefully using SIGKILL
+ """
+ self.logger.debug("Killing daemons using SIGKILL..")
+ return self.net.killRouterDaemons(daemons, wait, assertOnError)
+
+ def vtysh_cmd(self, command, isjson=False, daemon=None):
+ """
+ Runs the provided command string in the vty shell and returns a string
+ with the response.
+
+ This function also accepts multiple commands, but this mode does not
+ return output for each command. See vtysh_multicmd() for more details.
+ """
+ # Detect multi line commands
+ if command.find("\n") != -1:
+ return self.vtysh_multicmd(command, daemon=daemon)
+
+ dparam = ""
+ if daemon is not None:
+ dparam += "-d {}".format(daemon)
+
+ vtysh_command = 'vtysh {} -c "{}" 2>/dev/null'.format(dparam, command)
+
+ self.logger.info('vtysh command => "{}"'.format(command))
+ output = self.run(vtysh_command)
+
+ dbgout = output.strip()
+ if dbgout:
+ if "\n" in dbgout:
+ dbgout = dbgout.replace("\n", "\n\t")
+ self.logger.info("vtysh result:\n\t{}".format(dbgout))
+ else:
+ self.logger.info('vtysh result: "{}"'.format(dbgout))
+
+ if isjson is False:
+ return output
+
+ try:
+ return json.loads(output)
+ except ValueError as error:
+ logger.warning(
+ "vtysh_cmd: %s: failed to convert json output: %s: %s",
+ self.name,
+ str(output),
+ str(error),
+ )
+ return {}
+
+ def vtysh_multicmd(self, commands, pretty_output=True, daemon=None):
+ """
+ Runs the provided commands in the vty shell and return the result of
+ execution.
+
+ pretty_output: defines how the return value will be presented. When
+ True it will show the command as they were executed in the vty shell,
+ otherwise it will only show lines that failed.
+ """
+ # Prepare the temporary file that will hold the commands
+ fname = topotest.get_file(commands)
+
+ dparam = ""
+ if daemon is not None:
+ dparam += "-d {}".format(daemon)
+
+ # Run the commands and delete the temporary file
+ if pretty_output:
+ vtysh_command = "vtysh {} < {}".format(dparam, fname)
+ else:
+ vtysh_command = "vtysh {} -f {}".format(dparam, fname)
+
+ dbgcmds = commands if is_string(commands) else "\n".join(commands)
+ dbgcmds = "\t" + dbgcmds.replace("\n", "\n\t")
+ self.logger.info("vtysh command => FILE:\n{}".format(dbgcmds))
+
+ res = self.run(vtysh_command)
+ os.unlink(fname)
+
+ dbgres = res.strip()
+ if dbgres:
+ if "\n" in dbgres:
+ dbgres = dbgres.replace("\n", "\n\t")
+ self.logger.info("vtysh result:\n\t{}".format(dbgres))
+ else:
+ self.logger.info('vtysh result: "{}"'.format(dbgres))
+ return res
+
+ def report_memory_leaks(self, testname):
+ """
+ Runs the router memory leak check test. Has the following parameter:
+ testname: the test file name for identification
+
+ NOTE: to run this you must have the environment variable
+ TOPOTESTS_CHECK_MEMLEAK set or memleak_path configured in `pytest.ini`.
+ """
+ memleak_file = (
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK") or self.params["memleak_path"]
+ )
+ if memleak_file == "" or memleak_file == None:
+ return
+
+ self.stop()
+
+ self.logger.info("running memory leak report")
+ self.net.report_memory_leaks(memleak_file, testname)
+
+ def version_info(self):
+ "Get equipment information from 'show version'."
+ output = self.vtysh_cmd("show version").split("\n")[0]
+ columns = topotest.normalize_text(output).split(" ")
+ try:
+ return {
+ "type": columns[0],
+ "version": columns[1],
+ }
+ except IndexError:
+ return {
+ "type": None,
+ "version": None,
+ }
+
+ def has_version(self, cmpop, version):
+ """
+ Compares router version using operation `cmpop` with `version`.
+ Valid `cmpop` values:
+ * `>=`: has the same version or greater
+ * '>': has greater version
+ * '=': has the same version
+ * '<': has a lesser version
+ * '<=': has the same version or lesser
+
+ Usage example: router.has_version('>', '1.0')
+ """
+ return self.net.checkRouterVersion(cmpop, version)
+
+ def has_type(self, rtype):
+ """
+ Compares router type with `rtype`. Returns `True` if the type matches,
+ otherwise `false`.
+ """
+ curtype = self.version_info()["type"]
+ return rtype == curtype
+
+ def has_mpls(self):
+ return self.net.hasmpls
+
+
+class TopoSwitch(TopoGear):
+ """
+ Switch abstraction. Has the following properties:
+ * cls: switch class that will be used to instantiate
+ * name: switch name
+ """
+
+ # pylint: disable=too-few-public-methods
+
+ def __init__(self, tgen, name, **params):
+ super(TopoSwitch, self).__init__(tgen, name, **params)
+ tgen.net.add_switch(name)
+
+ def __str__(self):
+ gear = super(TopoSwitch, self).__str__()
+ gear += " TopoSwitch<>"
+ return gear
+
+
+class TopoHost(TopoGear):
+ "Host abstraction."
+ # pylint: disable=too-few-public-methods
+
+ def __init__(self, tgen, name, **params):
+ """
+ Mininet has the following known `params` for hosts:
+ * `ip`: the IP address (string) for the host interface
+ * `defaultRoute`: the default route that will be installed
+ (e.g. 'via 10.0.0.1')
+ * `privateDirs`: directories that will be mounted on a different domain
+ (e.g. '/etc/important_dir').
+ """
+ super(TopoHost, self).__init__(tgen, name, **params)
+
+ # Propagate the router log directory
+ logfile = self._setup_tmpdir()
+ params["logdir"] = self.logdir
+
+ # Odd to have 2 logfiles for each host
+ self.logger = topolog.get_logger(name, log_level="debug", target=logfile)
+ params["logger"] = self.logger
+ tgen.net.add_host(name, **params)
+ topotest.fix_netns_limits(tgen.net[name])
+
+ # Mount gear log directory on a common path
+ self.net.bind_mount(self.gearlogdir, "/tmp/gearlogdir")
+
+ def __str__(self):
+ gear = super(TopoHost, self).__str__()
+ gear += ' TopoHost<ip="{}",defaultRoute="{}",privateDirs="{}">'.format(
+ self.params["ip"],
+ self.params["defaultRoute"],
+ str(self.params["privateDirs"]),
+ )
+ return gear
+
+
+class TopoExaBGP(TopoHost):
+ "ExaBGP peer abstraction."
+ # pylint: disable=too-few-public-methods
+
+ PRIVATE_DIRS = [
+ "/etc/exabgp",
+ "/var/run/exabgp",
+ "/var/log",
+ ]
+
+ def __init__(self, tgen, name, **params):
+ """
+ ExaBGP usually uses the following parameters:
+ * `ip`: the IP address (string) for the host interface
+ * `defaultRoute`: the default route that will be installed
+ (e.g. 'via 10.0.0.1')
+
+ Note: the different between a host and a ExaBGP peer is that this class
+ has a privateDirs already defined and contains functions to handle ExaBGP
+ things.
+ """
+ params["privateDirs"] = self.PRIVATE_DIRS
+ super(TopoExaBGP, self).__init__(tgen, name, **params)
+
+ def __str__(self):
+ gear = super(TopoExaBGP, self).__str__()
+ gear += " TopoExaBGP<>".format()
+ return gear
+
+ def start(self, peer_dir, env_file=None):
+ """
+ Start running ExaBGP daemon:
+ * Copy all peer* folder contents into /etc/exabgp
+ * Copy exabgp env file if specified
+ * Make all python files runnable
+ * Run ExaBGP with env file `env_file` and configuration peer*/exabgp.cfg
+ """
+ exacmd = self.tgen.get_exabgp_cmd()
+ assert exacmd, "Can't find a usabel ExaBGP (must be < version 4)"
+
+ self.run("mkdir -p /etc/exabgp")
+ self.run("chmod 755 /etc/exabgp")
+ self.run("cp {}/exa-* /etc/exabgp/".format(CWD))
+ self.run("cp {}/* /etc/exabgp/".format(peer_dir))
+ if env_file is not None:
+ self.run("cp {} /etc/exabgp/exabgp.env".format(env_file))
+ self.run("chmod 644 /etc/exabgp/*")
+ self.run("chmod a+x /etc/exabgp/*.py")
+ self.run("chown -R exabgp:exabgp /etc/exabgp")
+
+ output = self.run(exacmd + " -e /etc/exabgp/exabgp.env /etc/exabgp/exabgp.cfg")
+ if output == None or len(output) == 0:
+ output = "<none>"
+
+ logger.info("{} exabgp started, output={}".format(self.name, output))
+
+ def stop(self, wait=True, assertOnError=True):
+ "Stop ExaBGP peer and kill the daemon"
+ self.run("kill `cat /var/run/exabgp/exabgp.pid`")
+ return ""
+
+
+#
+# Diagnostic function
+#
+
+# Disable linter branch warning. It is expected to have these here.
+# pylint: disable=R0912
+def diagnose_env_linux(rundir):
+ """
+ Run diagnostics in the running environment. Returns `True` when everything
+ is ok, otherwise `False`.
+ """
+ ret = True
+
+ # Load configuration
+ config = configparser.ConfigParser(defaults=tgen_defaults)
+ pytestini_path = os.path.join(CWD, "../pytest.ini")
+ config.read(pytestini_path)
+
+ # Test log path exists before installing handler.
+ os.system("mkdir -p " + rundir)
+ # Log diagnostics to file so it can be examined later.
+ fhandler = logging.FileHandler(filename="{}/diagnostics.txt".format(rundir))
+ fhandler.setLevel(logging.DEBUG)
+ fhandler.setFormatter(logging.Formatter(fmt=topolog.FORMAT))
+ logger.addHandler(fhandler)
+
+ logger.info("Running environment diagnostics")
+
+ # Assert that we are running as root
+ if os.getuid() != 0:
+ logger.error("you must run topotest as root")
+ ret = False
+
+ # Assert that we have mininet
+ # if os.system("which mn >/dev/null 2>/dev/null") != 0:
+ # logger.error("could not find mininet binary (mininet is not installed)")
+ # ret = False
+
+ # Assert that we have iproute installed
+ if os.system("which ip >/dev/null 2>/dev/null") != 0:
+ logger.error("could not find ip binary (iproute is not installed)")
+ ret = False
+
+ # Assert that we have gdb installed
+ if os.system("which gdb >/dev/null 2>/dev/null") != 0:
+ logger.error("could not find gdb binary (gdb is not installed)")
+ ret = False
+
+ # Assert that FRR utilities exist
+ frrdir = config.get("topogen", "frrdir")
+ if not os.path.isdir(frrdir):
+ logger.error("could not find {} directory".format(frrdir))
+ ret = False
+ else:
+ try:
+ pwd.getpwnam("frr")[2]
+ except KeyError:
+ logger.warning('could not find "frr" user')
+
+ try:
+ grp.getgrnam("frr")[2]
+ except KeyError:
+ logger.warning('could not find "frr" group')
+
+ try:
+ if "frr" not in grp.getgrnam("frrvty").gr_mem:
+ logger.error(
+ '"frr" user and group exist, but user is not under "frrvty"'
+ )
+ except KeyError:
+ logger.warning('could not find "frrvty" group')
+
+ for fname in [
+ "zebra",
+ "ospfd",
+ "ospf6d",
+ "bgpd",
+ "ripd",
+ "ripngd",
+ "isisd",
+ "pimd",
+ "pim6d",
+ "ldpd",
+ "pbrd",
+ ]:
+ path = os.path.join(frrdir, fname)
+ if not os.path.isfile(path):
+ # LDPd is an exception
+ if fname == "ldpd":
+ logger.info(
+ "could not find {} in {}".format(fname, frrdir)
+ + "(LDPd tests will not run)"
+ )
+ continue
+
+ logger.error("could not find {} in {}".format(fname, frrdir))
+ ret = False
+ else:
+ if fname != "zebra":
+ continue
+
+ os.system("{} -v 2>&1 >{}/frr_zebra.txt".format(path, rundir))
+
+ # Test MPLS availability
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.5") < 0:
+ logger.info(
+ 'LDPd tests will not run (have kernel "{}", but it requires 4.5)'.format(
+ krel
+ )
+ )
+
+ # Test for MPLS Kernel modules available
+ if not topotest.module_present("mpls-router", load=False) != 0:
+ logger.info("LDPd tests will not run (missing mpls-router kernel module)")
+ if not topotest.module_present("mpls-iptunnel", load=False) != 0:
+ logger.info("LDPd tests will not run (missing mpls-iptunnel kernel module)")
+
+ if not get_exabgp_cmd():
+ logger.warning("Failed to find exabgp < 4")
+
+ logger.removeHandler(fhandler)
+ fhandler.close()
+
+ return ret
+
+
+def diagnose_env_freebsd():
+ return True
+
+
+def diagnose_env(rundir):
+ if sys.platform.startswith("linux"):
+ return diagnose_env_linux(rundir)
+ elif sys.platform.startswith("freebsd"):
+ return diagnose_env_freebsd()
+
+ return False
diff --git a/tests/topotests/lib/topojson.py b/tests/topotests/lib/topojson.py
new file mode 100644
index 0000000..b49b09e
--- /dev/null
+++ b/tests/topotests/lib/topojson.py
@@ -0,0 +1,420 @@
+#
+# Modified work Copyright (c) 2019 by VMware, Inc. ("VMware")
+# Original work Copyright (c) 2018 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 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.
+#
+
+import json
+import ipaddress
+import os
+from collections import OrderedDict
+from copy import deepcopy
+from re import search as re_search
+
+import pytest
+
+from lib.bgp import create_router_bgp
+from lib.common_config import (
+ create_bgp_community_lists,
+ create_interfaces_cfg,
+ create_prefix_lists,
+ create_route_maps,
+ create_static_routes,
+ create_vrf_cfg,
+ load_config_to_routers,
+ start_topology,
+ topo_daemons,
+ number_to_column,
+)
+from lib.ospf import create_router_ospf
+from lib.pim import (
+ create_igmp_config,
+ create_pim_config,
+ create_mld_config,
+)
+from lib.topolog import logger
+
+
+def build_topo_from_json(tgen, topo=None):
+ """
+ Reads configuration from JSON file. Adds routers, creates interface
+ names dynamically and link routers as defined in JSON to create
+ topology. Assigns IPs dynamically to all interfaces of each router.
+ * `tgen`: Topogen object
+ * `topo`: json file data, or use tgen.json_topo if None
+ """
+ if topo is None:
+ topo = tgen.json_topo
+
+ router_list = sorted(
+ topo["routers"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0))
+ )
+
+ switch_list = []
+ if "switches" in topo:
+ switch_list = sorted(
+ topo["switches"].keys(), key=lambda x: int(re_search(r"\d+", x).group(0))
+ )
+
+ listRouters = sorted(router_list[:])
+ listSwitches = sorted(switch_list[:])
+ listAllRouters = deepcopy(listRouters)
+ dictSwitches = {}
+
+ for routerN in router_list:
+ logger.info("Topo: Add router {}".format(routerN))
+ tgen.add_router(routerN)
+
+ for switchN in switch_list:
+ logger.info("Topo: Add switch {}".format(switchN))
+ dictSwitches[switchN] = tgen.add_switch(switchN)
+
+ if "ipv4base" in topo:
+ ipv4Next = ipaddress.IPv4Address(topo["link_ip_start"]["ipv4"])
+ ipv4Step = 2 ** (32 - topo["link_ip_start"]["v4mask"])
+ if topo["link_ip_start"]["v4mask"] < 32:
+ ipv4Next += 1
+ if "ipv6base" in topo:
+ ipv6Next = ipaddress.IPv6Address(topo["link_ip_start"]["ipv6"])
+ ipv6Step = 2 ** (128 - topo["link_ip_start"]["v6mask"])
+ if topo["link_ip_start"]["v6mask"] < 127:
+ ipv6Next += 1
+ for router in listRouters:
+ topo["routers"][router]["nextIfname"] = 0
+
+ router_count = 0
+ while listRouters != []:
+ curRouter = listRouters.pop(0)
+ # Physical Interfaces
+ if "links" in topo["routers"][curRouter]:
+ for destRouterLink, data in sorted(
+ topo["routers"][curRouter]["links"].items()
+ ):
+ currRouter_lo_json = topo["routers"][curRouter]["links"][destRouterLink]
+ # Loopback interfaces
+ if "type" in data and data["type"] == "loopback":
+ router_count += 1
+ if (
+ "ipv4" in currRouter_lo_json
+ and currRouter_lo_json["ipv4"] == "auto"
+ ):
+ currRouter_lo_json["ipv4"] = "{}{}.{}/{}".format(
+ topo["lo_prefix"]["ipv4"],
+ router_count,
+ number_to_column(curRouter),
+ topo["lo_prefix"]["v4mask"],
+ )
+ if (
+ "ipv6" in currRouter_lo_json
+ and currRouter_lo_json["ipv6"] == "auto"
+ ):
+ currRouter_lo_json["ipv6"] = "{}{}:{}/{}".format(
+ topo["lo_prefix"]["ipv6"],
+ router_count,
+ number_to_column(curRouter),
+ topo["lo_prefix"]["v6mask"],
+ )
+
+ if "-" in destRouterLink:
+ # Spliting and storing destRouterLink data in tempList
+ tempList = destRouterLink.split("-")
+
+ # destRouter
+ destRouter = tempList.pop(0)
+
+ # Current Router Link
+ tempList.insert(0, curRouter)
+ curRouterLink = "-".join(tempList)
+ else:
+ destRouter = destRouterLink
+ curRouterLink = curRouter
+
+ if destRouter in listRouters:
+ currRouter_link_json = topo["routers"][curRouter]["links"][
+ destRouterLink
+ ]
+ destRouter_link_json = topo["routers"][destRouter]["links"][
+ curRouterLink
+ ]
+
+ # Assigning name to interfaces
+ currRouter_link_json["interface"] = "{}-{}-eth{}".format(
+ curRouter, destRouter, topo["routers"][curRouter]["nextIfname"]
+ )
+ destRouter_link_json["interface"] = "{}-{}-eth{}".format(
+ destRouter, curRouter, topo["routers"][destRouter]["nextIfname"]
+ )
+
+ # add link interface
+ destRouter_link_json["peer-interface"] = "{}-{}-eth{}".format(
+ curRouter, destRouter, topo["routers"][curRouter]["nextIfname"]
+ )
+ currRouter_link_json["peer-interface"] = "{}-{}-eth{}".format(
+ destRouter, curRouter, topo["routers"][destRouter]["nextIfname"]
+ )
+
+ topo["routers"][curRouter]["nextIfname"] += 1
+ topo["routers"][destRouter]["nextIfname"] += 1
+
+ # Linking routers to each other as defined in JSON file
+ tgen.gears[curRouter].add_link(
+ tgen.gears[destRouter],
+ topo["routers"][curRouter]["links"][destRouterLink][
+ "interface"
+ ],
+ topo["routers"][destRouter]["links"][curRouterLink][
+ "interface"
+ ],
+ )
+
+ # IPv4
+ if "ipv4" in currRouter_link_json:
+ if currRouter_link_json["ipv4"] == "auto":
+ currRouter_link_json["ipv4"] = "{}/{}".format(
+ ipv4Next, topo["link_ip_start"]["v4mask"]
+ )
+ destRouter_link_json["ipv4"] = "{}/{}".format(
+ ipv4Next + 1, topo["link_ip_start"]["v4mask"]
+ )
+ ipv4Next += ipv4Step
+ # IPv6
+ if "ipv6" in currRouter_link_json:
+ if currRouter_link_json["ipv6"] == "auto":
+ currRouter_link_json["ipv6"] = "{}/{}".format(
+ ipv6Next, topo["link_ip_start"]["v6mask"]
+ )
+ destRouter_link_json["ipv6"] = "{}/{}".format(
+ ipv6Next + 1, topo["link_ip_start"]["v6mask"]
+ )
+ ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step)
+
+ logger.debug(
+ "Generated link data for router: %s\n%s",
+ curRouter,
+ json.dumps(
+ topo["routers"][curRouter]["links"], indent=4, sort_keys=True
+ ),
+ )
+
+ switch_count = 0
+ add_switch_to_topo = []
+ while listSwitches != []:
+ curSwitch = listSwitches.pop(0)
+ # Physical Interfaces
+ if "links" in topo["switches"][curSwitch]:
+ for destRouterLink, data in sorted(
+ topo["switches"][curSwitch]["links"].items()
+ ):
+
+ # Loopback interfaces
+ if "dst_node" in data:
+ destRouter = data["dst_node"]
+
+ elif "-" in destRouterLink:
+ # Spliting and storing destRouterLink data in tempList
+ tempList = destRouterLink.split("-")
+ # destRouter
+ destRouter = tempList.pop(0)
+ else:
+ destRouter = destRouterLink
+
+ if destRouter in listAllRouters:
+
+ topo["routers"][destRouter]["links"][curSwitch] = deepcopy(
+ topo["switches"][curSwitch]["links"][destRouterLink]
+ )
+
+ # Assigning name to interfaces
+ topo["routers"][destRouter]["links"][curSwitch][
+ "interface"
+ ] = "{}-{}-eth{}".format(
+ destRouter, curSwitch, topo["routers"][destRouter]["nextIfname"]
+ )
+
+ topo["switches"][curSwitch]["links"][destRouter][
+ "interface"
+ ] = "{}-{}-eth{}".format(
+ curSwitch, destRouter, topo["routers"][destRouter]["nextIfname"]
+ )
+
+ topo["routers"][destRouter]["nextIfname"] += 1
+
+ # Add links
+ dictSwitches[curSwitch].add_link(
+ tgen.gears[destRouter],
+ topo["switches"][curSwitch]["links"][destRouter]["interface"],
+ topo["routers"][destRouter]["links"][curSwitch]["interface"],
+ )
+
+ # IPv4
+ if "ipv4" in topo["routers"][destRouter]["links"][curSwitch]:
+ if (
+ topo["routers"][destRouter]["links"][curSwitch]["ipv4"]
+ == "auto"
+ ):
+ topo["routers"][destRouter]["links"][curSwitch][
+ "ipv4"
+ ] = "{}/{}".format(
+ ipv4Next, topo["link_ip_start"]["v4mask"]
+ )
+ ipv4Next += 1
+ # IPv6
+ if "ipv6" in topo["routers"][destRouter]["links"][curSwitch]:
+ if (
+ topo["routers"][destRouter]["links"][curSwitch]["ipv6"]
+ == "auto"
+ ):
+ topo["routers"][destRouter]["links"][curSwitch][
+ "ipv6"
+ ] = "{}/{}".format(
+ ipv6Next, topo["link_ip_start"]["v6mask"]
+ )
+ ipv6Next = ipaddress.IPv6Address(int(ipv6Next) + ipv6Step)
+
+ logger.debug(
+ "Generated link data for router: %s\n%s",
+ curRouter,
+ json.dumps(
+ topo["routers"][curRouter]["links"], indent=4, sort_keys=True
+ ),
+ )
+
+
+def linux_intf_config_from_json(tgen, topo=None):
+ """Configure interfaces from linux based on topo."""
+ if topo is None:
+ topo = tgen.json_topo
+
+ routers = topo["routers"]
+ for rname in routers:
+ router = tgen.net[rname]
+ links = routers[rname]["links"]
+ for rrname in links:
+ link = links[rrname]
+ if rrname == "lo":
+ lname = "lo"
+ else:
+ lname = link["interface"]
+ if "ipv4" in link:
+ router.cmd_raises("ip addr add {} dev {}".format(link["ipv4"], lname))
+ if "ipv6" in link:
+ router.cmd_raises(
+ "ip -6 addr add {} dev {}".format(link["ipv6"], lname)
+ )
+
+
+def build_config_from_json(tgen, topo=None, save_bkup=True):
+ """
+ Reads initial configuraiton from JSON for each router, builds
+ configuration and loads its to router.
+
+ * `tgen`: Topogen object
+ * `topo`: json file data, or use tgen.json_topo if None
+ """
+
+ func_dict = OrderedDict(
+ [
+ ("vrfs", create_vrf_cfg),
+ ("links", create_interfaces_cfg),
+ ("static_routes", create_static_routes),
+ ("prefix_lists", create_prefix_lists),
+ ("bgp_community_list", create_bgp_community_lists),
+ ("route_maps", create_route_maps),
+ ("pim", create_pim_config),
+ ("igmp", create_igmp_config),
+ ("mld", create_mld_config),
+ ("bgp", create_router_bgp),
+ ("ospf", create_router_ospf),
+ ]
+ )
+
+ if topo is None:
+ topo = tgen.json_topo
+
+ data = topo["routers"]
+ for func_type in func_dict.keys():
+ logger.info("Checking for {} configuration in input data".format(func_type))
+
+ func_dict.get(func_type)(tgen, data, build=True)
+
+ routers = sorted(topo["routers"].keys())
+ result = load_config_to_routers(tgen, routers, save_bkup)
+ if not result:
+ logger.info("build_config_from_json: failed to configure topology")
+ pytest.exit(1)
+
+ logger.info(
+ "Built config now clearing ospf neighbors as that router-id might not be what is used"
+ )
+ for ospf in ["ospf", "ospf6"]:
+ for router in data:
+ if ospf not in data[router]:
+ continue
+
+ r = tgen.gears[router]
+ if ospf == "ospf":
+ r.vtysh_cmd("clear ip ospf process")
+ else:
+ r.vtysh_cmd("clear ipv6 ospf6 process")
+
+
+def create_tgen_from_json(testfile, json_file=None):
+ """Create a topogen object given a testfile.
+
+ - `testfile` : The path to the testfile.
+ - `json_file` : The path to the json config file. If None the pathname is derived
+ from the `testfile` first by trying to replace `.py` by `.json` and if that isn't
+ present then by removing `test_` prefix as well.
+ """
+ from lib.topogen import Topogen # Topogen imports this module too
+
+ thisdir = os.path.dirname(os.path.realpath(testfile))
+ basename = os.path.basename(testfile)
+ logger.debug("starting standard JSON based module setup for %s", basename)
+
+ assert basename.startswith("test_")
+ assert basename.endswith(".py")
+ json_file = os.path.join(thisdir, basename[:-3] + ".json")
+ if not os.path.exists(json_file):
+ json_file = os.path.join(thisdir, basename[5:-3] + ".json")
+ assert os.path.exists(json_file)
+ with open(json_file, "r") as topof:
+ topo = json.load(topof)
+
+ # Create topology
+ tgen = Topogen(lambda tgen: build_topo_from_json(tgen, topo), basename[:-3])
+ tgen.json_topo = topo
+ return tgen
+
+
+def setup_module_from_json(testfile, json_file=None):
+ """Do the standard module setup for JSON based test.
+
+ * `testfile` : The path to the testfile. The name is used to derive the json config
+ file name as well (removing `test_` prefix and replacing `.py` suffix with `.json`
+ """
+ # Create topology object
+ tgen = create_tgen_from_json(testfile, json_file)
+
+ # Start routers (and their daemons)
+ start_topology(tgen, topo_daemons(tgen))
+
+ # Configure routers
+ build_config_from_json(tgen)
+ assert not tgen.routers_have_failure()
+
+ return tgen
diff --git a/tests/topotests/lib/topolog.py b/tests/topotests/lib/topolog.py
new file mode 100644
index 0000000..9cc3386
--- /dev/null
+++ b/tests/topotests/lib/topolog.py
@@ -0,0 +1,178 @@
+#
+# topolog.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2017 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.
+#
+
+"""
+Logging utilities for topology tests.
+
+This file defines our logging abstraction.
+"""
+
+import logging
+import os
+import subprocess
+import sys
+
+if sys.version_info[0] > 2:
+ pass
+else:
+ pass
+
+try:
+ from xdist import is_xdist_controller
+except ImportError:
+
+ def is_xdist_controller():
+ return False
+
+
+BASENAME = "topolog"
+
+# Helper dictionary to convert Topogen logging levels to Python's logging.
+DEBUG_TOPO2LOGGING = {
+ "debug": logging.DEBUG,
+ "info": logging.INFO,
+ "output": logging.INFO,
+ "warning": logging.WARNING,
+ "error": logging.ERROR,
+ "critical": logging.CRITICAL,
+}
+FORMAT = "%(asctime)s.%(msecs)03d %(levelname)s: %(name)s: %(message)s"
+
+handlers = {}
+logger = logging.getLogger("topolog")
+
+
+def set_handler(l, target=None):
+ if target is None:
+ h = logging.NullHandler()
+ else:
+ if isinstance(target, str):
+ h = logging.FileHandler(filename=target, mode="w")
+ else:
+ h = logging.StreamHandler(stream=target)
+ h.setFormatter(logging.Formatter(fmt=FORMAT))
+ # Don't filter anything at the handler level
+ h.setLevel(logging.DEBUG)
+ l.addHandler(h)
+ return h
+
+
+def set_log_level(l, level):
+ "Set the logging level."
+ # Messages sent to this logger only are created if this level or above.
+ log_level = DEBUG_TOPO2LOGGING.get(level, level)
+ l.setLevel(log_level)
+
+
+def get_logger(name, log_level=None, target=None):
+ l = logging.getLogger("{}.{}".format(BASENAME, name))
+
+ if log_level is not None:
+ set_log_level(l, log_level)
+
+ if target is not None:
+ set_handler(l, target)
+
+ return l
+
+
+# nodeid: all_protocol_startup/test_all_protocol_startup.py::test_router_running
+
+
+def get_test_logdir(nodeid=None):
+ """Get log directory relative pathname."""
+ xdist_worker = os.getenv("PYTEST_XDIST_WORKER", "")
+ mode = os.getenv("PYTEST_XDIST_MODE", "no")
+
+ if not nodeid:
+ nodeid = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
+
+ cur_test = nodeid.replace("[", "_").replace("]", "_")
+ path, testname = cur_test.split("::")
+ path = path[:-3].replace("/", ".")
+
+ # We use different logdir paths based on how xdist is running.
+ if mode == "each":
+ return os.path.join(path, testname, xdist_worker)
+ elif mode == "load":
+ return os.path.join(path, testname)
+ else:
+ assert (
+ mode == "no" or mode == "loadfile" or mode == "loadscope"
+ ), "Unknown dist mode {}".format(mode)
+
+ return path
+
+
+def logstart(nodeid, location, rundir):
+ """Called from pytest before module setup."""
+
+ mode = os.getenv("PYTEST_XDIST_MODE", "no")
+ worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
+
+ # We only per-test log in the workers (or non-dist)
+ if not worker and mode != "no":
+ return
+
+ handler_id = nodeid + worker
+ assert handler_id not in handlers
+
+ rel_log_dir = get_test_logdir(nodeid)
+ exec_log_dir = os.path.join(rundir, rel_log_dir)
+ subprocess.check_call(
+ "mkdir -p {0} && chmod 1777 {0}".format(exec_log_dir), shell=True
+ )
+ exec_log_path = os.path.join(exec_log_dir, "exec.log")
+
+ # Add test based exec log handler
+ h = set_handler(logger, exec_log_path)
+ handlers[handler_id] = h
+
+ if worker:
+ logger.info(
+ "Logging on worker %s for %s into %s", worker, handler_id, exec_log_path
+ )
+ else:
+ logger.info("Logging for %s into %s", handler_id, exec_log_path)
+
+
+def logfinish(nodeid, location):
+ """Called from pytest after module teardown."""
+ # This function may not be called if pytest is interrupted.
+
+ worker = os.getenv("PYTEST_TOPOTEST_WORKER", "")
+ handler_id = nodeid + worker
+
+ if handler_id in handlers:
+ # Remove test based exec log handler
+ if worker:
+ logger.info("Closing logs for %s", handler_id)
+
+ h = handlers[handler_id]
+ logger.removeHandler(handlers[handler_id])
+ h.flush()
+ h.close()
+ del handlers[handler_id]
+
+
+console_handler = set_handler(logger, None)
+set_log_level(logger, "debug")
diff --git a/tests/topotests/lib/topotest.py b/tests/topotests/lib/topotest.py
new file mode 100644
index 0000000..5a3f586
--- /dev/null
+++ b/tests/topotests/lib/topotest.py
@@ -0,0 +1,2175 @@
+#!/usr/bin/env python
+
+#
+# topotest.py
+# Library of helper functions for NetDEF Topology Tests
+#
+# Copyright (c) 2016 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.
+#
+
+import difflib
+import errno
+import functools
+import glob
+import json
+import os
+import pdb
+import platform
+import re
+import resource
+import signal
+import subprocess
+import sys
+import tempfile
+import time
+from copy import deepcopy
+
+import lib.topolog as topolog
+from lib.topolog import logger
+
+if sys.version_info[0] > 2:
+ import configparser
+ from collections.abc import Mapping
+else:
+ import ConfigParser as configparser
+ from collections import Mapping
+
+from lib import micronet
+from lib.micronet_compat import Node
+
+g_extra_config = {}
+
+
+def get_logs_path(rundir):
+ logspath = topolog.get_test_logdir()
+ return os.path.join(rundir, logspath)
+
+
+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"
+
+ def __init__(self):
+ self.errors = []
+
+ def add_error(self, error):
+ "Append error message to the result"
+ for line in error.splitlines():
+ self.errors.append(line)
+
+ def has_errors(self):
+ "Returns True if there were errors, otherwise False."
+ return len(self.errors) > 0
+
+ def gen_report(self):
+ headline = ["Generated JSON diff error report:", ""]
+ return headline + self.errors
+
+ def __str__(self):
+ return (
+ "Generated JSON diff error report:\n\n\n" + "\n".join(self.errors) + "\n\n"
+ )
+
+
+def gen_json_diff_report(d1, d2, exact=False, path="> $", acc=(0, "")):
+ """
+ Internal workhorse which compares two JSON data structures and generates an error report suited to be read by a human eye.
+ """
+
+ def dump_json(v):
+ if isinstance(v, (dict, list)):
+ return "\t" + "\t".join(
+ json.dumps(v, indent=4, separators=(",", ": ")).splitlines(True)
+ )
+ else:
+ return "'{}'".format(v)
+
+ def json_type(v):
+ if isinstance(v, (list, tuple)):
+ return "Array"
+ elif isinstance(v, dict):
+ return "Object"
+ elif isinstance(v, (int, float)):
+ return "Number"
+ elif isinstance(v, bool):
+ return "Boolean"
+ elif isinstance(v, str):
+ return "String"
+ elif v == None:
+ return "null"
+
+ def get_errors(other_acc):
+ return other_acc[1]
+
+ def get_errors_n(other_acc):
+ return other_acc[0]
+
+ def add_error(acc, msg, points=1):
+ return (acc[0] + points, acc[1] + "{}: {}\n".format(path, msg))
+
+ def merge_errors(acc, other_acc):
+ return (acc[0] + other_acc[0], acc[1] + other_acc[1])
+
+ def add_idx(idx):
+ return "{}[{}]".format(path, idx)
+
+ def add_key(key):
+ return "{}->{}".format(path, key)
+
+ def has_errors(other_acc):
+ return other_acc[0] > 0
+
+ if d2 == "*" or (
+ not isinstance(d1, (list, dict))
+ and not isinstance(d2, (list, dict))
+ and d1 == d2
+ ):
+ return acc
+ elif (
+ not isinstance(d1, (list, dict))
+ and not isinstance(d2, (list, dict))
+ and d1 != d2
+ ):
+ acc = add_error(
+ acc,
+ "d1 has element with value '{}' but in d2 it has value '{}'".format(d1, d2),
+ )
+ elif (
+ isinstance(d1, list)
+ and isinstance(d2, list)
+ and ((len(d2) > 0 and d2[0] == "__ordered__") or exact)
+ ):
+ if not exact:
+ del d2[0]
+ if len(d1) != len(d2):
+ acc = add_error(
+ acc,
+ "d1 has Array of length {} but in d2 it is of length {}".format(
+ len(d1), len(d2)
+ ),
+ )
+ else:
+ for idx, v1, v2 in zip(range(0, len(d1)), d1, d2):
+ acc = merge_errors(
+ acc, gen_json_diff_report(v1, v2, exact=exact, path=add_idx(idx))
+ )
+ elif isinstance(d1, list) and isinstance(d2, list):
+ if len(d1) < len(d2):
+ acc = add_error(
+ acc,
+ "d1 has Array of length {} but in d2 it is of length {}".format(
+ len(d1), len(d2)
+ ),
+ )
+ else:
+ for idx2, v2 in zip(range(0, len(d2)), d2):
+ found_match = False
+ closest_diff = None
+ closest_idx = None
+ for idx1, v1 in zip(range(0, len(d1)), d1):
+ tmp_v1 = deepcopy(v1)
+ tmp_v2 = deepcopy(v2)
+ tmp_diff = gen_json_diff_report(tmp_v1, tmp_v2, path=add_idx(idx1))
+ if not has_errors(tmp_diff):
+ found_match = True
+ del d1[idx1]
+ break
+ elif not closest_diff or get_errors_n(tmp_diff) < get_errors_n(
+ closest_diff
+ ):
+ closest_diff = tmp_diff
+ closest_idx = idx1
+ if not found_match and isinstance(v2, (list, dict)):
+ sub_error = "\n\n\t{}".format(
+ "\t".join(get_errors(closest_diff).splitlines(True))
+ )
+ acc = add_error(
+ acc,
+ (
+ "d2 has the following element at index {} which is not present in d1: "
+ + "\n\n{}\n\n\tClosest match in d1 is at index {} with the following errors: {}"
+ ).format(idx2, dump_json(v2), closest_idx, sub_error),
+ )
+ if not found_match and not isinstance(v2, (list, dict)):
+ acc = add_error(
+ acc,
+ "d2 has the following element at index {} which is not present in d1: {}".format(
+ idx2, dump_json(v2)
+ ),
+ )
+ elif isinstance(d1, dict) and isinstance(d2, dict) and exact:
+ invalid_keys_d1 = [k for k in d1.keys() if k not in d2.keys()]
+ invalid_keys_d2 = [k for k in d2.keys() if k not in d1.keys()]
+ for k in invalid_keys_d1:
+ acc = add_error(acc, "d1 has key '{}' which is not present in d2".format(k))
+ for k in invalid_keys_d2:
+ acc = add_error(acc, "d2 has key '{}' which is not present in d1".format(k))
+ valid_keys_intersection = [k for k in d1.keys() if k in d2.keys()]
+ for k in valid_keys_intersection:
+ acc = merge_errors(
+ acc, gen_json_diff_report(d1[k], d2[k], exact=exact, path=add_key(k))
+ )
+ elif isinstance(d1, dict) and isinstance(d2, dict):
+ none_keys = [k for k, v in d2.items() if v == None]
+ none_keys_present = [k for k in d1.keys() if k in none_keys]
+ for k in none_keys_present:
+ acc = add_error(
+ acc, "d1 has key '{}' which is not supposed to be present".format(k)
+ )
+ keys = [k for k, v in d2.items() if v != None]
+ invalid_keys_intersection = [k for k in keys if k not in d1.keys()]
+ for k in invalid_keys_intersection:
+ acc = add_error(acc, "d2 has key '{}' which is not present in d1".format(k))
+ valid_keys_intersection = [k for k in keys if k in d1.keys()]
+ for k in valid_keys_intersection:
+ acc = merge_errors(
+ acc, gen_json_diff_report(d1[k], d2[k], exact=exact, path=add_key(k))
+ )
+ else:
+ acc = add_error(
+ acc,
+ "d1 has element of type '{}' but the corresponding element in d2 is of type '{}'".format(
+ json_type(d1), json_type(d2)
+ ),
+ points=2,
+ )
+
+ return acc
+
+
+def json_cmp(d1, d2, exact=False):
+ """
+ JSON compare function. Receives two parameters:
+ * `d1`: parsed JSON data structure
+ * `d2`: parsed JSON data structure
+
+ Returns 'None' when all JSON Object keys and all Array elements of d2 have a match
+ in d1, i.e., when d2 is a "subset" of d1 without honoring any order. Otherwise an
+ error report is generated and wrapped in a 'json_cmp_result()'. There are special
+ parameters and notations explained below which can be used to cover rather unusual
+ cases:
+
+ * when 'exact is set to 'True' then d1 and d2 are tested for equality (including
+ order within JSON Arrays)
+ * using 'null' (or 'None' in Python) as JSON Object value is checking for key
+ absence in d1
+ * using '*' as JSON Object value or Array value is checking for presence in d1
+ without checking the values
+ * using '__ordered__' as first element in a JSON Array in d2 will also check the
+ order when it is compared to an Array in d1
+ """
+
+ (errors_n, errors) = gen_json_diff_report(deepcopy(d1), deepcopy(d2), exact=exact)
+
+ if errors_n > 0:
+ result = json_cmp_result()
+ result.add_error(errors)
+ return result
+ else:
+ return None
+
+
+def router_output_cmp(router, cmd, expected):
+ """
+ Runs `cmd` in router and compares the output with `expected`.
+ """
+ return difflines(
+ normalize_text(router.vtysh_cmd(cmd)),
+ normalize_text(expected),
+ title1="Current output",
+ title2="Expected output",
+ )
+
+
+def router_json_cmp(router, cmd, data, exact=False):
+ """
+ Runs `cmd` that returns JSON data (normally the command ends with 'json')
+ and compare with `data` contents.
+ """
+ return json_cmp(router.vtysh_cmd(cmd, isjson=True), data, exact)
+
+
+def run_and_expect(func, what, count=20, wait=3):
+ """
+ Run `func` and compare the result with `what`. Do it for `count` times
+ waiting `wait` seconds between tries. By default it tries 20 times with
+ 3 seconds delay between tries.
+
+ Returns (True, func-return) on success or
+ (False, func-return) on failure.
+
+ ---
+
+ Helper functions to use with this function:
+ - router_output_cmp
+ - router_json_cmp
+ """
+ start_time = time.time()
+ func_name = "<unknown>"
+ if func.__class__ == functools.partial:
+ func_name = func.func.__name__
+ else:
+ func_name = func.__name__
+
+ logger.info(
+ "'{}' polling started (interval {} secs, maximum {} tries)".format(
+ func_name, wait, count
+ )
+ )
+
+ while count > 0:
+ result = func()
+ if result != what:
+ time.sleep(wait)
+ count -= 1
+ continue
+
+ end_time = time.time()
+ logger.info(
+ "'{}' succeeded after {:.2f} seconds".format(
+ func_name, end_time - start_time
+ )
+ )
+ return (True, result)
+
+ end_time = time.time()
+ logger.error(
+ "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time)
+ )
+ return (False, result)
+
+
+def run_and_expect_type(func, etype, count=20, wait=3, avalue=None):
+ """
+ Run `func` and compare the result with `etype`. Do it for `count` times
+ waiting `wait` seconds between tries. By default it tries 20 times with
+ 3 seconds delay between tries.
+
+ This function is used when you want to test the return type and,
+ optionally, the return value.
+
+ Returns (True, func-return) on success or
+ (False, func-return) on failure.
+ """
+ start_time = time.time()
+ func_name = "<unknown>"
+ if func.__class__ == functools.partial:
+ func_name = func.func.__name__
+ else:
+ func_name = func.__name__
+
+ logger.info(
+ "'{}' polling started (interval {} secs, maximum wait {} secs)".format(
+ func_name, wait, int(wait * count)
+ )
+ )
+
+ while count > 0:
+ result = func()
+ if not isinstance(result, etype):
+ logger.debug(
+ "Expected result type '{}' got '{}' instead".format(etype, type(result))
+ )
+ time.sleep(wait)
+ count -= 1
+ continue
+
+ if etype != type(None) and avalue != None and result != avalue:
+ logger.debug("Expected value '{}' got '{}' instead".format(avalue, result))
+ time.sleep(wait)
+ count -= 1
+ continue
+
+ end_time = time.time()
+ logger.info(
+ "'{}' succeeded after {:.2f} seconds".format(
+ func_name, end_time - start_time
+ )
+ )
+ return (True, result)
+
+ end_time = time.time()
+ logger.error(
+ "'{}' failed after {:.2f} seconds".format(func_name, end_time - start_time)
+ )
+ return (False, result)
+
+
+def router_json_cmp_retry(router, cmd, data, exact=False, retry_timeout=10.0):
+ """
+ Runs `cmd` that returns JSON data (normally the command ends with 'json')
+ and compare with `data` contents. Retry by default for 10 seconds
+ """
+
+ def test_func():
+ return router_json_cmp(router, cmd, data, exact)
+
+ ok, _ = run_and_expect(test_func, None, int(retry_timeout), 1)
+ return ok
+
+
+def int2dpid(dpid):
+ "Converting Integer to DPID"
+
+ try:
+ dpid = hex(dpid)[2:]
+ dpid = "0" * (16 - len(dpid)) + dpid
+ return dpid
+ except IndexError:
+ raise Exception(
+ "Unable to derive default datapath ID - "
+ "please either specify a dpid or use a "
+ "canonical switch name such as s23."
+ )
+
+
+def pid_exists(pid):
+ "Check whether pid exists in the current process table."
+
+ 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:
+ # ESRCH == No such process
+ return False
+ elif err.errno == errno.EPERM:
+ # EPERM clearly means there's a process to deny access to
+ return True
+ else:
+ # According to "man 2 kill" possible error values are
+ # (EINVAL, EPERM, ESRCH)
+ raise
+ else:
+ return True
+
+
+def get_textdiff(text1, text2, title1="", title2="", **opts):
+ "Returns empty string if same or formatted diff"
+
+ diff = "\n".join(
+ difflib.unified_diff(text1, text2, fromfile=title1, tofile=title2, **opts)
+ )
+ # Clean up line endings
+ diff = os.linesep.join([s for s in diff.splitlines() if s])
+ return diff
+
+
+def difflines(text1, text2, title1="", title2="", **opts):
+ "Wrapper for get_textdiff to avoid string transformations."
+ text1 = ("\n".join(text1.rstrip().splitlines()) + "\n").splitlines(1)
+ text2 = ("\n".join(text2.rstrip().splitlines()) + "\n").splitlines(1)
+ return get_textdiff(text1, text2, title1, title2, **opts)
+
+
+def get_file(content):
+ """
+ Generates a temporary file in '/tmp' with `content` and returns the file name.
+ """
+ if isinstance(content, list) or isinstance(content, tuple):
+ content = "\n".join(content)
+ fde = tempfile.NamedTemporaryFile(mode="w", delete=False)
+ fname = fde.name
+ fde.write(content)
+ fde.close()
+ return fname
+
+
+def normalize_text(text):
+ """
+ Strips formating spaces/tabs, carriage returns and trailing whitespace.
+ """
+ text = re.sub(r"[ \t]+", " ", text)
+ text = re.sub(r"\r", "", text)
+
+ # Remove whitespace in the middle of text.
+ text = re.sub(r"[ \t]+\n", "\n", text)
+ # Remove whitespace at the end of the text.
+ text = text.rstrip()
+
+ return text
+
+
+def is_linux():
+ """
+ Parses unix name output to check if running on GNU/Linux.
+
+ Returns True if running on Linux, returns False otherwise.
+ """
+
+ if os.uname()[0] == "Linux":
+ return True
+ return False
+
+
+def iproute2_is_vrf_capable():
+ """
+ Checks if the iproute2 version installed on the system is capable of
+ handling VRFs by interpreting the output of the 'ip' utility found in PATH.
+
+ Returns True if capability can be detected, returns False otherwise.
+ """
+
+ if is_linux():
+ try:
+ subp = subprocess.Popen(
+ ["ip", "route", "show", "vrf"],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ )
+ iproute2_err = subp.communicate()[1].splitlines()[0].split()[0]
+
+ if iproute2_err != "Error:":
+ return True
+ except Exception:
+ pass
+ return False
+
+
+def module_present_linux(module, load):
+ """
+ Returns whether `module` is present.
+
+ If `load` is true, it will try to load it via modprobe.
+ """
+ with open("/proc/modules", "r") as modules_file:
+ if module.replace("-", "_") in modules_file.read():
+ return True
+ cmd = "/sbin/modprobe {}{}".format("" if load else "-n ", module)
+ if os.system(cmd) != 0:
+ return False
+ else:
+ return True
+
+
+def module_present_freebsd(module, load):
+ return True
+
+
+def module_present(module, load=True):
+ if sys.platform.startswith("linux"):
+ return module_present_linux(module, load)
+ elif sys.platform.startswith("freebsd"):
+ return module_present_freebsd(module, load)
+
+
+def version_cmp(v1, v2):
+ """
+ Compare two version strings and returns:
+
+ * `-1`: if `v1` is less than `v2`
+ * `0`: if `v1` is equal to `v2`
+ * `1`: if `v1` is greater than `v2`
+
+ Raises `ValueError` if versions are not well formated.
+ """
+ vregex = r"(?P<whole>\d+(\.(\d+))*)"
+ v1m = re.match(vregex, v1)
+ v2m = re.match(vregex, v2)
+ if v1m is None or v2m is None:
+ raise ValueError("got a invalid version string")
+
+ # Split values
+ v1g = v1m.group("whole").split(".")
+ v2g = v2m.group("whole").split(".")
+
+ # Get the longest version string
+ vnum = len(v1g)
+ if len(v2g) > vnum:
+ vnum = len(v2g)
+
+ # Reverse list because we are going to pop the tail
+ v1g.reverse()
+ v2g.reverse()
+ for _ in range(vnum):
+ try:
+ v1n = int(v1g.pop())
+ except IndexError:
+ while v2g:
+ v2n = int(v2g.pop())
+ if v2n > 0:
+ return -1
+ break
+
+ try:
+ v2n = int(v2g.pop())
+ except IndexError:
+ if v1n > 0:
+ return 1
+ while v1g:
+ v1n = int(v1g.pop())
+ if v1n > 0:
+ return 1
+ break
+
+ if v1n > v2n:
+ return 1
+ if v1n < v2n:
+ return -1
+ return 0
+
+
+def interface_set_status(node, ifacename, ifaceaction=False, vrf_name=None):
+ if ifaceaction:
+ str_ifaceaction = "no shutdown"
+ else:
+ str_ifaceaction = "shutdown"
+ if vrf_name == None:
+ cmd = 'vtysh -c "configure terminal" -c "interface {0}" -c "{1}"'.format(
+ ifacename, str_ifaceaction
+ )
+ else:
+ cmd = (
+ 'vtysh -c "configure terminal" -c "interface {0} vrf {1}" -c "{2}"'.format(
+ ifacename, vrf_name, str_ifaceaction
+ )
+ )
+ node.run(cmd)
+
+
+def ip4_route_zebra(node, vrf_name=None):
+ """
+ Gets an output of 'show ip route' command. It can be used
+ with comparing the output to a reference
+ """
+ if vrf_name == None:
+ tmp = node.vtysh_cmd("show ip route")
+ else:
+ tmp = node.vtysh_cmd("show ip route vrf {0}".format(vrf_name))
+ output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp)
+
+ lines = output.splitlines()
+ header_found = False
+ while lines and (not lines[0].strip() or not header_found):
+ if "o - offload failure" in lines[0]:
+ header_found = True
+ lines = lines[1:]
+ return "\n".join(lines)
+
+
+def ip6_route_zebra(node, vrf_name=None):
+ """
+ Retrieves the output of 'show ipv6 route [vrf vrf_name]', then
+ canonicalizes it by eliding link-locals.
+ """
+
+ if vrf_name == None:
+ tmp = node.vtysh_cmd("show ipv6 route")
+ else:
+ tmp = node.vtysh_cmd("show ipv6 route vrf {0}".format(vrf_name))
+
+ # Mask out timestamp
+ output = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", tmp)
+
+ # Mask out the link-local addresses
+ output = re.sub(r"fe80::[^ ]+,", "fe80::XXXX:XXXX:XXXX:XXXX,", output)
+
+ lines = output.splitlines()
+ header_found = False
+ while lines and (not lines[0].strip() or not header_found):
+ if "o - offload failure" in lines[0]:
+ header_found = True
+ lines = lines[1:]
+
+ return "\n".join(lines)
+
+
+def proto_name_to_number(protocol):
+ return {
+ "bgp": "186",
+ "isis": "187",
+ "ospf": "188",
+ "rip": "189",
+ "ripng": "190",
+ "nhrp": "191",
+ "eigrp": "192",
+ "ldp": "193",
+ "sharp": "194",
+ "pbr": "195",
+ "static": "196",
+ "ospf6": "197",
+ }.get(
+ protocol, protocol
+ ) # default return same as input
+
+
+def ip4_route(node):
+ """
+ Gets a structured return of the command 'ip route'. It can be used in
+ conjunction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ {
+ '10.0.1.0/24': {
+ 'dev': 'eth0',
+ 'via': '172.16.0.1',
+ 'proto': '188',
+ },
+ '10.0.2.0/24': {
+ 'dev': 'eth1',
+ 'proto': 'kernel',
+ }
+ }
+ """
+ output = normalize_text(node.run("ip route")).splitlines()
+ result = {}
+ for line in output:
+ columns = line.split(" ")
+ route = result[columns[0]] = {}
+ prev = None
+ for column in columns:
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
+ # translate protocol names back to numbers
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "scope":
+ route["scope"] = column
+ prev = column
+
+ return result
+
+
+def ip4_vrf_route(node):
+ """
+ Gets a structured return of the command 'ip route show vrf {0}-cust1'.
+ It can be used in conjunction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ {
+ '10.0.1.0/24': {
+ 'dev': 'eth0',
+ 'via': '172.16.0.1',
+ 'proto': '188',
+ },
+ '10.0.2.0/24': {
+ 'dev': 'eth1',
+ 'proto': 'kernel',
+ }
+ }
+ """
+ output = normalize_text(
+ node.run("ip route show vrf {0}-cust1".format(node.name))
+ ).splitlines()
+
+ result = {}
+ for line in output:
+ columns = line.split(" ")
+ route = result[columns[0]] = {}
+ prev = None
+ for column in columns:
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
+ # translate protocol names back to numbers
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "scope":
+ route["scope"] = column
+ prev = column
+
+ return result
+
+
+def ip6_route(node):
+ """
+ Gets a structured return of the command 'ip -6 route'. It can be used in
+ conjunction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ {
+ '2001:db8:1::/64': {
+ 'dev': 'eth0',
+ 'proto': '188',
+ },
+ '2001:db8:2::/64': {
+ 'dev': 'eth1',
+ 'proto': 'kernel',
+ }
+ }
+ """
+ output = normalize_text(node.run("ip -6 route")).splitlines()
+ result = {}
+ for line in output:
+ columns = line.split(" ")
+ route = result[columns[0]] = {}
+ prev = None
+ for column in columns:
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
+ # translate protocol names back to numbers
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "pref":
+ route["pref"] = column
+ prev = column
+
+ return result
+
+
+def ip6_vrf_route(node):
+ """
+ Gets a structured return of the command 'ip -6 route show vrf {0}-cust1'.
+ It can be used in conjunction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ {
+ '2001:db8:1::/64': {
+ 'dev': 'eth0',
+ 'proto': '188',
+ },
+ '2001:db8:2::/64': {
+ 'dev': 'eth1',
+ 'proto': 'kernel',
+ }
+ }
+ """
+ output = normalize_text(
+ node.run("ip -6 route show vrf {0}-cust1".format(node.name))
+ ).splitlines()
+ result = {}
+ for line in output:
+ columns = line.split(" ")
+ route = result[columns[0]] = {}
+ prev = None
+ for column in columns:
+ if prev == "dev":
+ route["dev"] = column
+ if prev == "via":
+ route["via"] = column
+ if prev == "proto":
+ # translate protocol names back to numbers
+ route["proto"] = proto_name_to_number(column)
+ if prev == "metric":
+ route["metric"] = column
+ if prev == "pref":
+ route["pref"] = column
+ prev = column
+
+ return result
+
+
+def ip_rules(node):
+ """
+ Gets a structured return of the command 'ip rule'. It can be used in
+ conjunction with json_cmp() to provide accurate assert explanations.
+
+ Return example:
+ [
+ {
+ "pref": "0"
+ "from": "all"
+ },
+ {
+ "pref": "32766"
+ "from": "all"
+ },
+ {
+ "to": "3.4.5.0/24",
+ "iif": "r1-eth2",
+ "pref": "304",
+ "from": "1.2.0.0/16",
+ "proto": "zebra"
+ }
+ ]
+ """
+ output = normalize_text(node.run("ip rule")).splitlines()
+ result = []
+ for line in output:
+ columns = line.split(" ")
+
+ route = {}
+ # remove last character, since it is ':'
+ pref = columns[0][:-1]
+ route["pref"] = pref
+ prev = None
+ for column in columns:
+ if prev == "from":
+ route["from"] = column
+ if prev == "to":
+ route["to"] = column
+ if prev == "proto":
+ route["proto"] = column
+ if prev == "iif":
+ route["iif"] = column
+ if prev == "fwmark":
+ route["fwmark"] = column
+ prev = column
+
+ result.append(route)
+ return result
+
+
+def sleep(amount, reason=None):
+ """
+ Sleep wrapper that registers in the log the amount of sleep
+ """
+ if reason is None:
+ logger.info("Sleeping for {} seconds".format(amount))
+ else:
+ logger.info(reason + " ({} seconds)".format(amount))
+
+ time.sleep(amount)
+
+
+def checkAddressSanitizerError(output, router, component, logdir=""):
+ "Checks for AddressSanitizer in output. If found, then logs it and returns true, false otherwise"
+
+ def processAddressSanitizerError(asanErrorRe, output, router, component):
+ sys.stderr.write(
+ "%s: %s triggered an exception by AddressSanitizer\n" % (router, component)
+ )
+ # Sanitizer Error found in log
+ pidMark = asanErrorRe.group(1)
+ addressSanitizerLog = re.search(
+ "%s(.*)%s" % (pidMark, pidMark), output, re.DOTALL
+ )
+ if addressSanitizerLog:
+ # Find Calling Test. Could be multiple steps back
+ testframe = sys._current_frames().values()[0]
+ level = 0
+ while level < 10:
+ test = os.path.splitext(
+ os.path.basename(testframe.f_globals["__file__"])
+ )[0]
+ if (test != "topotest") and (test != "topogen"):
+ # Found the calling test
+ callingTest = os.path.basename(testframe.f_globals["__file__"])
+ break
+ level = level + 1
+ testframe = testframe.f_back
+ if level >= 10:
+ # somehow couldn't find the test script.
+ callingTest = "unknownTest"
+ #
+ # Now finding Calling Procedure
+ level = 0
+ while level < 20:
+ callingProc = sys._getframe(level).f_code.co_name
+ if (
+ (callingProc != "processAddressSanitizerError")
+ and (callingProc != "checkAddressSanitizerError")
+ and (callingProc != "checkRouterCores")
+ and (callingProc != "stopRouter")
+ and (callingProc != "stop")
+ and (callingProc != "stop_topology")
+ and (callingProc != "checkRouterRunning")
+ and (callingProc != "check_router_running")
+ and (callingProc != "routers_have_failure")
+ ):
+ # Found the calling test
+ break
+ level = level + 1
+ if level >= 20:
+ # something wrong - couldn't found the calling test function
+ callingProc = "unknownProc"
+ with open("/tmp/AddressSanitzer.txt", "a") as addrSanFile:
+ sys.stderr.write(
+ "AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n"
+ % (callingTest, callingProc, router)
+ )
+ sys.stderr.write(
+ "\n".join(addressSanitizerLog.group(1).splitlines()) + "\n"
+ )
+ addrSanFile.write("## Error: %s\n\n" % asanErrorRe.group(2))
+ addrSanFile.write(
+ "### AddressSanitizer error in topotest `%s`, test `%s`, router `%s`\n\n"
+ % (callingTest, callingProc, router)
+ )
+ addrSanFile.write(
+ " "
+ + "\n ".join(addressSanitizerLog.group(1).splitlines())
+ + "\n"
+ )
+ addrSanFile.write("\n---------------\n")
+ return
+
+ addressSanitizerError = re.search(
+ r"(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", output
+ )
+ if addressSanitizerError:
+ processAddressSanitizerError(addressSanitizerError, output, router, component)
+ return True
+
+ # No Address Sanitizer Error in Output. Now check for AddressSanitizer daemon file
+ if logdir:
+ filepattern = logdir + "/" + router + "/" + component + ".asan.*"
+ logger.debug(
+ "Log check for %s on %s, pattern %s\n" % (component, router, filepattern)
+ )
+ for file in glob.glob(filepattern):
+ with open(file, "r") as asanErrorFile:
+ asanError = asanErrorFile.read()
+ addressSanitizerError = re.search(
+ r"(==[0-9]+==)ERROR: AddressSanitizer: ([^\s]*) ", asanError
+ )
+ if addressSanitizerError:
+ processAddressSanitizerError(
+ addressSanitizerError, asanError, router, component
+ )
+ return True
+ return False
+
+
+def _sysctl_atleast(commander, variable, min_value):
+ if isinstance(min_value, tuple):
+ min_value = list(min_value)
+ is_list = isinstance(min_value, list)
+
+ sval = commander.cmd_raises("sysctl -n " + variable).strip()
+ if is_list:
+ cur_val = [int(x) for x in sval.split()]
+ else:
+ cur_val = int(sval)
+
+ set_value = False
+ if is_list:
+ for i, v in enumerate(cur_val):
+ if v < min_value[i]:
+ set_value = True
+ else:
+ min_value[i] = v
+ else:
+ if cur_val < min_value:
+ set_value = True
+ if set_value:
+ if is_list:
+ valstr = " ".join([str(x) for x in min_value])
+ else:
+ valstr = str(min_value)
+ logger.info("Increasing sysctl %s from %s to %s", variable, cur_val, valstr)
+ commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr))
+
+
+def _sysctl_assure(commander, variable, value):
+ if isinstance(value, tuple):
+ value = list(value)
+ is_list = isinstance(value, list)
+
+ sval = commander.cmd_raises("sysctl -n " + variable).strip()
+ if is_list:
+ cur_val = [int(x) for x in sval.split()]
+ else:
+ cur_val = sval
+
+ set_value = False
+ if is_list:
+ for i, v in enumerate(cur_val):
+ if v != value[i]:
+ set_value = True
+ else:
+ value[i] = v
+ else:
+ if cur_val != str(value):
+ set_value = True
+
+ if set_value:
+ if is_list:
+ valstr = " ".join([str(x) for x in value])
+ else:
+ valstr = str(value)
+ logger.info("Changing sysctl %s from %s to %s", variable, cur_val, valstr)
+ commander.cmd_raises('sysctl -w {}="{}"\n'.format(variable, valstr))
+
+
+def sysctl_atleast(commander, variable, min_value, raises=False):
+ try:
+ if commander is None:
+ commander = micronet.Commander("topotest")
+ return _sysctl_atleast(commander, variable, min_value)
+ except subprocess.CalledProcessError as error:
+ logger.warning(
+ "%s: Failed to assure sysctl min value %s = %s",
+ commander,
+ variable,
+ min_value,
+ )
+ if raises:
+ raise
+
+
+def sysctl_assure(commander, variable, value, raises=False):
+ try:
+ if commander is None:
+ commander = micronet.Commander("topotest")
+ return _sysctl_assure(commander, variable, value)
+ except subprocess.CalledProcessError as error:
+ logger.warning(
+ "%s: Failed to assure sysctl value %s = %s",
+ commander,
+ variable,
+ value,
+ exc_info=True,
+ )
+ if raises:
+ raise
+
+
+def rlimit_atleast(rname, min_value, raises=False):
+ try:
+ cval = resource.getrlimit(rname)
+ soft, hard = cval
+ if soft < min_value:
+ nval = (min_value, hard if min_value < hard else min_value)
+ logger.info("Increasing rlimit %s from %s to %s", rname, cval, nval)
+ resource.setrlimit(rname, nval)
+ except subprocess.CalledProcessError as error:
+ logger.warning(
+ "Failed to assure rlimit [%s] = %s", rname, min_value, exc_info=True
+ )
+ if raises:
+ raise
+
+
+def fix_netns_limits(ns):
+
+ # Maximum read and write socket buffer sizes
+ sysctl_atleast(ns, "net.ipv4.tcp_rmem", [10 * 1024, 87380, 16 * 2 ** 20])
+ sysctl_atleast(ns, "net.ipv4.tcp_wmem", [10 * 1024, 87380, 16 * 2 ** 20])
+
+ sysctl_assure(ns, "net.ipv4.conf.all.rp_filter", 0)
+ sysctl_assure(ns, "net.ipv4.conf.default.rp_filter", 0)
+ sysctl_assure(ns, "net.ipv4.conf.lo.rp_filter", 0)
+
+ sysctl_assure(ns, "net.ipv4.conf.all.forwarding", 1)
+ sysctl_assure(ns, "net.ipv4.conf.default.forwarding", 1)
+
+ # XXX if things fail look here as this wasn't done previously
+ sysctl_assure(ns, "net.ipv6.conf.all.forwarding", 1)
+ sysctl_assure(ns, "net.ipv6.conf.default.forwarding", 1)
+
+ # ARP
+ sysctl_assure(ns, "net.ipv4.conf.default.arp_announce", 2)
+ sysctl_assure(ns, "net.ipv4.conf.default.arp_notify", 1)
+ # Setting this to 1 breaks topotests that rely on lo addresses being proxy arp'd for
+ sysctl_assure(ns, "net.ipv4.conf.default.arp_ignore", 0)
+ sysctl_assure(ns, "net.ipv4.conf.all.arp_announce", 2)
+ sysctl_assure(ns, "net.ipv4.conf.all.arp_notify", 1)
+ # Setting this to 1 breaks topotests that rely on lo addresses being proxy arp'd for
+ sysctl_assure(ns, "net.ipv4.conf.all.arp_ignore", 0)
+
+ sysctl_assure(ns, "net.ipv4.icmp_errors_use_inbound_ifaddr", 1)
+
+ # Keep ipv6 permanent addresses on an admin down
+ sysctl_assure(ns, "net.ipv6.conf.all.keep_addr_on_down", 1)
+ if version_cmp(platform.release(), "4.20") >= 0:
+ sysctl_assure(ns, "net.ipv6.route.skip_notify_on_dev_down", 1)
+
+ sysctl_assure(ns, "net.ipv4.conf.all.ignore_routes_with_linkdown", 1)
+ sysctl_assure(ns, "net.ipv6.conf.all.ignore_routes_with_linkdown", 1)
+
+ # igmp
+ sysctl_atleast(ns, "net.ipv4.igmp_max_memberships", 1000)
+
+ # Use neigh information on selection of nexthop for multipath hops
+ sysctl_assure(ns, "net.ipv4.fib_multipath_use_neigh", 1)
+
+
+def fix_host_limits():
+ """Increase system limits."""
+
+ rlimit_atleast(resource.RLIMIT_NPROC, 8 * 1024)
+ rlimit_atleast(resource.RLIMIT_NOFILE, 16 * 1024)
+ sysctl_atleast(None, "fs.file-max", 16 * 1024)
+ sysctl_atleast(None, "kernel.pty.max", 16 * 1024)
+
+ # Enable coredumps
+ # Original on ubuntu 17.x, but apport won't save as in namespace
+ # |/usr/share/apport/apport %p %s %c %d %P
+ sysctl_assure(None, "kernel.core_pattern", "%e_core-sig_%s-pid_%p.dmp")
+ sysctl_assure(None, "kernel.core_uses_pid", 1)
+ sysctl_assure(None, "fs.suid_dumpable", 1)
+
+ # Maximum connection backlog
+ sysctl_atleast(None, "net.core.netdev_max_backlog", 4 * 1024)
+
+ # Maximum read and write socket buffer sizes
+ sysctl_atleast(None, "net.core.rmem_max", 16 * 2 ** 20)
+ sysctl_atleast(None, "net.core.wmem_max", 16 * 2 ** 20)
+
+ # Garbage Collection Settings for ARP and Neighbors
+ sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh2", 4 * 1024)
+ sysctl_atleast(None, "net.ipv4.neigh.default.gc_thresh3", 8 * 1024)
+ sysctl_atleast(None, "net.ipv6.neigh.default.gc_thresh2", 4 * 1024)
+ sysctl_atleast(None, "net.ipv6.neigh.default.gc_thresh3", 8 * 1024)
+ # Hold entries for 10 minutes
+ sysctl_assure(None, "net.ipv4.neigh.default.base_reachable_time_ms", 10 * 60 * 1000)
+ sysctl_assure(None, "net.ipv6.neigh.default.base_reachable_time_ms", 10 * 60 * 1000)
+
+ # igmp
+ sysctl_assure(None, "net.ipv4.neigh.default.mcast_solicit", 10)
+
+ # MLD
+ sysctl_atleast(None, "net.ipv6.mld_max_msf", 512)
+
+ # Increase routing table size to 128K
+ sysctl_atleast(None, "net.ipv4.route.max_size", 128 * 1024)
+ sysctl_atleast(None, "net.ipv6.route.max_size", 128 * 1024)
+
+
+def setup_node_tmpdir(logdir, name):
+ # Cleanup old log, valgrind, and core files.
+ subprocess.check_call(
+ "rm -rf {0}/{1}.valgrind.* {1}.*.asan {0}/{1}/".format(logdir, name), shell=True
+ )
+
+ # Setup the per node directory.
+ nodelogdir = "{}/{}".format(logdir, name)
+ subprocess.check_call(
+ "mkdir -p {0} && chmod 1777 {0}".format(nodelogdir), shell=True
+ )
+ logfile = "{0}/{1}.log".format(logdir, name)
+ return logfile
+
+
+class Router(Node):
+ "A Node with IPv4/IPv6 forwarding enabled"
+
+ def __init__(self, name, **params):
+
+ # Backward compatibility:
+ # Load configuration defaults like topogen.
+ self.config_defaults = configparser.ConfigParser(
+ defaults={
+ "verbosity": "info",
+ "frrdir": "/usr/lib/frr",
+ "routertype": "frr",
+ "memleak_path": "",
+ }
+ )
+
+ self.config_defaults.read(
+ os.path.join(os.path.dirname(os.path.realpath(__file__)), "../pytest.ini")
+ )
+
+ # If this topology is using old API and doesn't have logdir
+ # specified, then attempt to generate an unique logdir.
+ self.logdir = params.get("logdir")
+ if self.logdir is None:
+ self.logdir = get_logs_path(g_extra_config["rundir"])
+
+ if not params.get("logger"):
+ # If logger is present topogen has already set this up
+ logfile = setup_node_tmpdir(self.logdir, name)
+ l = topolog.get_logger(name, log_level="debug", target=logfile)
+ params["logger"] = l
+
+ super(Router, self).__init__(name, **params)
+
+ self.daemondir = None
+ self.hasmpls = False
+ self.routertype = "frr"
+ self.unified_config = None
+ self.daemons = {
+ "zebra": 0,
+ "ripd": 0,
+ "ripngd": 0,
+ "ospfd": 0,
+ "ospf6d": 0,
+ "isisd": 0,
+ "bgpd": 0,
+ "pimd": 0,
+ "pim6d": 0,
+ "ldpd": 0,
+ "eigrpd": 0,
+ "nhrpd": 0,
+ "staticd": 0,
+ "bfdd": 0,
+ "sharpd": 0,
+ "babeld": 0,
+ "pbrd": 0,
+ "pathd": 0,
+ "snmpd": 0,
+ }
+ self.daemons_options = {"zebra": ""}
+ self.reportCores = True
+ self.version = None
+
+ self.ns_cmd = "sudo nsenter -a -t {} ".format(self.pid)
+ try:
+ # Allow escaping from running inside docker
+ cgroup = open("/proc/1/cgroup").read()
+ m = re.search("[0-9]+:cpuset:/docker/([a-f0-9]+)", cgroup)
+ if m:
+ self.ns_cmd = "docker exec -it {} ".format(m.group(1)) + self.ns_cmd
+ except IOError:
+ pass
+ else:
+ logger.debug("CMD to enter {}: {}".format(self.name, self.ns_cmd))
+
+ def _config_frr(self, **params):
+ "Configure FRR binaries"
+ self.daemondir = params.get("frrdir")
+ if self.daemondir is None:
+ self.daemondir = self.config_defaults.get("topogen", "frrdir")
+
+ zebra_path = os.path.join(self.daemondir, "zebra")
+ if not os.path.isfile(zebra_path):
+ raise Exception("FRR zebra binary doesn't exist at {}".format(zebra_path))
+
+ # pylint: disable=W0221
+ # Some params are only meaningful for the parent class.
+ def config(self, **params):
+ super(Router, self).config(**params)
+
+ # User did not specify the daemons directory, try to autodetect it.
+ self.daemondir = params.get("daemondir")
+ if self.daemondir is None:
+ self.routertype = params.get(
+ "routertype", self.config_defaults.get("topogen", "routertype")
+ )
+ self._config_frr(**params)
+ else:
+ # Test the provided path
+ zpath = os.path.join(self.daemondir, "zebra")
+ if not os.path.isfile(zpath):
+ raise Exception("No zebra binary found in {}".format(zpath))
+ # Allow user to specify routertype when the path was specified.
+ if params.get("routertype") is not None:
+ self.routertype = params.get("routertype")
+
+ # Set ownership of config files
+ self.cmd("chown {0}:{0}vty /etc/{0}".format(self.routertype))
+
+ def terminate(self):
+ # Stop running FRR daemons
+ self.stopRouter()
+ super(Router, self).terminate()
+ os.system("chmod -R go+rw " + self.logdir)
+
+ # Return count of running daemons
+ def listDaemons(self):
+ ret = []
+ rc, stdout, _ = self.cmd_status(
+ "ls -1 /var/run/%s/*.pid" % self.routertype, warn=False
+ )
+ if rc:
+ return ret
+ for d in stdout.strip().split("\n"):
+ pidfile = d.strip()
+ try:
+ pid = int(self.cmd_raises("cat %s" % pidfile, warn=False).strip())
+ name = os.path.basename(pidfile[:-4])
+
+ # probably not compatible with bsd.
+ rc, _, _ = self.cmd_status("test -d /proc/{}".format(pid), warn=False)
+ if rc:
+ logger.warning(
+ "%s: %s exited leaving pidfile %s (%s)",
+ self.name,
+ name,
+ pidfile,
+ pid,
+ )
+ self.cmd("rm -- " + pidfile)
+ else:
+ ret.append((name, pid))
+ except (subprocess.CalledProcessError, ValueError):
+ pass
+ return ret
+
+ def stopRouter(self, assertOnError=True, minErrorVersion="5.1"):
+ # Stop Running FRR Daemons
+ running = self.listDaemons()
+ if not running:
+ return ""
+
+ logger.info("%s: stopping %s", self.name, ", ".join([x[0] for x in running]))
+ for name, pid in running:
+ logger.info("{}: sending SIGTERM to {}".format(self.name, name))
+ try:
+ os.kill(pid, signal.SIGTERM)
+ except OSError as err:
+ logger.info(
+ "%s: could not kill %s (%s): %s", self.name, name, pid, str(err)
+ )
+
+ running = self.listDaemons()
+ if running:
+ for _ in range(0, 30):
+ sleep(
+ 0.5,
+ "{}: waiting for daemons stopping: {}".format(
+ self.name, ", ".join([x[0] for x in running])
+ ),
+ )
+ running = self.listDaemons()
+ if not running:
+ break
+
+ if not running:
+ return ""
+
+ logger.warning(
+ "%s: sending SIGBUS to: %s", self.name, ", ".join([x[0] for x in running])
+ )
+ for name, pid in running:
+ pidfile = "/var/run/{}/{}.pid".format(self.routertype, name)
+ logger.info("%s: killing %s", self.name, name)
+ self.cmd("kill -SIGBUS %d" % pid)
+ self.cmd("rm -- " + pidfile)
+
+ sleep(
+ 0.5, "%s: waiting for daemons to exit/core after initial SIGBUS" % self.name
+ )
+
+ errors = self.checkRouterCores(reportOnce=True)
+ if self.checkRouterVersion("<", minErrorVersion):
+ # ignore errors in old versions
+ errors = ""
+ if assertOnError and (errors is not None) and len(errors) > 0:
+ assert "Errors found - details follow:" == 0, errors
+ return errors
+
+ def removeIPs(self):
+ for interface in self.intfNames():
+ try:
+ self.intf_ip_cmd(interface, "ip address flush " + interface)
+ except Exception as ex:
+ logger.error("%s can't remove IPs %s", self, str(ex))
+ # pdb.set_trace()
+ # assert False, "can't remove IPs %s" % str(ex)
+
+ def checkCapability(self, daemon, param):
+ if param is not None:
+ daemon_path = os.path.join(self.daemondir, daemon)
+ daemon_search_option = param.replace("-", "")
+ output = self.cmd(
+ "{0} -h | grep {1}".format(daemon_path, daemon_search_option)
+ )
+ if daemon_search_option not in output:
+ return False
+ return True
+
+ def loadConf(self, daemon, source=None, param=None):
+ """Enabled and set config for a daemon.
+
+ Arranges for loading of daemon configuration from the specified source. Possible
+ `source` values are `None` for an empty config file, a path name which is used
+ directly, or a file name with no path components which is first looked for
+ directly and then looked for under a sub-directory named after router.
+ """
+
+ # Unfortunately this API allowsfor source to not exist for any and all routers.
+ if source:
+ head, tail = os.path.split(source)
+ if not head and not self.path_exists(tail):
+ script_dir = os.environ["PYTEST_TOPOTEST_SCRIPTDIR"]
+ router_relative = os.path.join(script_dir, self.name, tail)
+ if self.path_exists(router_relative):
+ source = router_relative
+ self.logger.info(
+ "using router relative configuration: {}".format(source)
+ )
+
+ # print "Daemons before:", self.daemons
+ if daemon in self.daemons.keys() or daemon == "frr":
+ if daemon == "frr":
+ self.unified_config = 1
+ else:
+ self.daemons[daemon] = 1
+ if param is not None:
+ self.daemons_options[daemon] = param
+ conf_file = "/etc/{}/{}.conf".format(self.routertype, daemon)
+ if source is None or not os.path.exists(source):
+ if daemon == "frr" or not self.unified_config:
+ self.cmd_raises("rm -f " + conf_file)
+ self.cmd_raises("touch " + conf_file)
+ else:
+ self.cmd_raises("cp {} {}".format(source, conf_file))
+
+ if not self.unified_config or daemon == "frr":
+ self.cmd_raises("chown {0}:{0} {1}".format(self.routertype, conf_file))
+ self.cmd_raises("chmod 664 {}".format(conf_file))
+
+ if (daemon == "snmpd") and (self.routertype == "frr"):
+ # /etc/snmp is private mount now
+ self.cmd('echo "agentXSocket /etc/frr/agentx" >> /etc/snmp/frr.conf')
+ self.cmd('echo "mibs +ALL" > /etc/snmp/snmp.conf')
+
+ if (daemon == "zebra") and (self.daemons["staticd"] == 0):
+ # Add staticd with zebra - if it exists
+ try:
+ staticd_path = os.path.join(self.daemondir, "staticd")
+ except:
+ pdb.set_trace()
+
+ if os.path.isfile(staticd_path):
+ self.daemons["staticd"] = 1
+ self.daemons_options["staticd"] = ""
+ # Auto-Started staticd has no config, so it will read from zebra config
+ else:
+ logger.info("No daemon {} known".format(daemon))
+ # print "Daemons after:", self.daemons
+
+ def runInWindow(self, cmd, title=None):
+ return self.run_in_window(cmd, title)
+
+ def startRouter(self, tgen=None):
+ if self.unified_config:
+ self.cmd(
+ 'echo "service integrated-vtysh-config" >> /etc/%s/vtysh.conf'
+ % self.routertype
+ )
+ else:
+ # Disable integrated-vtysh-config
+ self.cmd(
+ 'echo "no service integrated-vtysh-config" >> /etc/%s/vtysh.conf'
+ % self.routertype
+ )
+
+ self.cmd(
+ "chown %s:%svty /etc/%s/vtysh.conf"
+ % (self.routertype, self.routertype, self.routertype)
+ )
+ # TODO remove the following lines after all tests are migrated to Topogen.
+ # Try to find relevant old logfiles in /tmp and delete them
+ map(os.remove, glob.glob("{}/{}/*.log".format(self.logdir, self.name)))
+ # Remove old core files
+ map(os.remove, glob.glob("{}/{}/*.dmp".format(self.logdir, self.name)))
+ # Remove IP addresses from OS first - we have them in zebra.conf
+ self.removeIPs()
+ # If ldp is used, check for LDP to be compiled and Linux Kernel to be 4.5 or higher
+ # No error - but return message and skip all the tests
+ if self.daemons["ldpd"] == 1:
+ ldpd_path = os.path.join(self.daemondir, "ldpd")
+ if not os.path.isfile(ldpd_path):
+ logger.info("LDP Test, but no ldpd compiled or installed")
+ return "LDP Test, but no ldpd compiled or installed"
+
+ if version_cmp(platform.release(), "4.5") < 0:
+ logger.info("LDP Test need Linux Kernel 4.5 minimum")
+ return "LDP Test need Linux Kernel 4.5 minimum"
+ # Check if have mpls
+ if tgen != None:
+ self.hasmpls = tgen.hasmpls
+ if self.hasmpls != True:
+ logger.info(
+ "LDP/MPLS Tests will be skipped, platform missing module(s)"
+ )
+ else:
+ # Test for MPLS Kernel modules available
+ self.hasmpls = False
+ if not module_present("mpls-router"):
+ logger.info(
+ "MPLS tests will not run (missing mpls-router kernel module)"
+ )
+ elif not module_present("mpls-iptunnel"):
+ logger.info(
+ "MPLS tests will not run (missing mpls-iptunnel kernel module)"
+ )
+ else:
+ self.hasmpls = True
+ if self.hasmpls != True:
+ return "LDP/MPLS Tests need mpls kernel modules"
+
+ # Really want to use sysctl_atleast here, but only when MPLS is actually being
+ # used
+ self.cmd("echo 100000 > /proc/sys/net/mpls/platform_labels")
+
+ shell_routers = g_extra_config["shell"]
+ if "all" in shell_routers or self.name in shell_routers:
+ self.run_in_window(os.getenv("SHELL", "bash"), title="sh-%s" % self.name)
+
+ if self.daemons["eigrpd"] == 1:
+ eigrpd_path = os.path.join(self.daemondir, "eigrpd")
+ if not os.path.isfile(eigrpd_path):
+ logger.info("EIGRP Test, but no eigrpd compiled or installed")
+ return "EIGRP Test, but no eigrpd compiled or installed"
+
+ if self.daemons["bfdd"] == 1:
+ bfdd_path = os.path.join(self.daemondir, "bfdd")
+ if not os.path.isfile(bfdd_path):
+ logger.info("BFD Test, but no bfdd compiled or installed")
+ return "BFD Test, but no bfdd compiled or installed"
+
+ status = self.startRouterDaemons(tgen=tgen)
+
+ vtysh_routers = g_extra_config["vtysh"]
+ if "all" in vtysh_routers or self.name in vtysh_routers:
+ self.run_in_window("vtysh", title="vt-%s" % self.name)
+
+ if self.unified_config:
+ self.cmd("vtysh -f /etc/frr/frr.conf")
+
+ return status
+
+ def getStdErr(self, daemon):
+ return self.getLog("err", daemon)
+
+ def getStdOut(self, daemon):
+ return self.getLog("out", daemon)
+
+ def getLog(self, log, daemon):
+ return self.cmd("cat {}/{}/{}.{}".format(self.logdir, self.name, daemon, log))
+
+ def startRouterDaemons(self, daemons=None, tgen=None):
+ "Starts FRR daemons for this router."
+
+ asan_abort = g_extra_config["asan_abort"]
+ gdb_breakpoints = g_extra_config["gdb_breakpoints"]
+ gdb_daemons = g_extra_config["gdb_daemons"]
+ gdb_routers = g_extra_config["gdb_routers"]
+ valgrind_extra = g_extra_config["valgrind_extra"]
+ valgrind_memleaks = g_extra_config["valgrind_memleaks"]
+ strace_daemons = g_extra_config["strace_daemons"]
+
+ # Get global bundle data
+ if not self.path_exists("/etc/frr/support_bundle_commands.conf"):
+ # Copy global value if was covered by namespace mount
+ bundle_data = ""
+ if os.path.exists("/etc/frr/support_bundle_commands.conf"):
+ with open("/etc/frr/support_bundle_commands.conf", "r") as rf:
+ bundle_data = rf.read()
+ self.cmd_raises(
+ "cat > /etc/frr/support_bundle_commands.conf",
+ stdin=bundle_data,
+ )
+
+ # Starts actual daemons without init (ie restart)
+ # cd to per node directory
+ self.cmd("install -m 775 -o frr -g frr -d {}/{}".format(self.logdir, self.name))
+ self.set_cwd("{}/{}".format(self.logdir, self.name))
+ self.cmd("umask 000")
+
+ # Re-enable to allow for report per run
+ self.reportCores = True
+
+ # XXX: glue code forward ported from removed function.
+ if self.version == None:
+ self.version = self.cmd(
+ os.path.join(self.daemondir, "bgpd") + " -v"
+ ).split()[2]
+ logger.info("{}: running version: {}".format(self.name, self.version))
+ # If `daemons` was specified then some upper API called us with
+ # specific daemons, otherwise just use our own configuration.
+ daemons_list = []
+ if daemons is not None:
+ daemons_list = daemons
+ else:
+ # Append all daemons configured.
+ for daemon in self.daemons:
+ if self.daemons[daemon] == 1:
+ daemons_list.append(daemon)
+
+ def start_daemon(daemon, extra_opts=None):
+ daemon_opts = self.daemons_options.get(daemon, "")
+ rediropt = " > {0}.out 2> {0}.err".format(daemon)
+ if daemon == "snmpd":
+ binary = "/usr/sbin/snmpd"
+ cmdenv = ""
+ cmdopt = "{} -C -c /etc/frr/snmpd.conf -p ".format(
+ daemon_opts
+ ) + "/var/run/{}/snmpd.pid -x /etc/frr/agentx".format(self.routertype)
+ else:
+ binary = os.path.join(self.daemondir, daemon)
+
+ cmdenv = "ASAN_OPTIONS="
+ if asan_abort:
+ cmdenv = "abort_on_error=1:"
+ cmdenv += "log_path={0}/{1}.{2}.asan ".format(
+ self.logdir, self.name, daemon
+ )
+
+ if valgrind_memleaks:
+ this_dir = os.path.dirname(
+ os.path.abspath(os.path.realpath(__file__))
+ )
+ supp_file = os.path.abspath(
+ os.path.join(this_dir, "../../../tools/valgrind.supp")
+ )
+ cmdenv += " /usr/bin/valgrind --num-callers=50 --log-file={1}/{2}.valgrind.{0}.%p --leak-check=full --suppressions={3}".format(
+ daemon, self.logdir, self.name, supp_file
+ )
+ if valgrind_extra:
+ cmdenv += (
+ " --gen-suppressions=all --expensive-definedness-checks=yes"
+ )
+ elif daemon in strace_daemons or "all" in strace_daemons:
+ cmdenv = "strace -f -D -o {1}/{2}.strace.{0} ".format(
+ daemon, self.logdir, self.name
+ )
+
+ cmdopt = "{} --command-log-always --log file:{}.log --log-level debug".format(
+ daemon_opts, daemon
+ )
+ if extra_opts:
+ cmdopt += " " + extra_opts
+
+ if (
+ (gdb_routers or gdb_daemons)
+ and (
+ not gdb_routers or self.name in gdb_routers or "all" in gdb_routers
+ )
+ and (not gdb_daemons or daemon in gdb_daemons or "all" in gdb_daemons)
+ ):
+ if daemon == "snmpd":
+ cmdopt += " -f "
+
+ cmdopt += rediropt
+ gdbcmd = "sudo -E gdb " + binary
+ if gdb_breakpoints:
+ gdbcmd += " -ex 'set breakpoint pending on'"
+ for bp in gdb_breakpoints:
+ gdbcmd += " -ex 'b {}'".format(bp)
+ gdbcmd += " -ex 'run {}'".format(cmdopt)
+
+ self.run_in_window(gdbcmd, daemon)
+
+ logger.info(
+ "%s: %s %s launched in gdb window", self, self.routertype, daemon
+ )
+ else:
+ if daemon != "snmpd":
+ cmdopt += " -d "
+ cmdopt += rediropt
+
+ try:
+ self.cmd_raises(" ".join([cmdenv, binary, cmdopt]), warn=False)
+ except subprocess.CalledProcessError as error:
+ self.logger.error(
+ '%s: Failed to launch "%s" daemon (%d) using: %s%s%s:',
+ self,
+ daemon,
+ error.returncode,
+ error.cmd,
+ '\n:stdout: "{}"'.format(error.stdout.strip())
+ if error.stdout
+ else "",
+ '\n:stderr: "{}"'.format(error.stderr.strip())
+ if error.stderr
+ else "",
+ )
+ else:
+ logger.info("%s: %s %s started", self, self.routertype, daemon)
+
+ # Start Zebra first
+ if "zebra" in daemons_list:
+ start_daemon("zebra", "-s 90000000")
+ while "zebra" in daemons_list:
+ daemons_list.remove("zebra")
+
+ # Start staticd next if required
+ if "staticd" in daemons_list:
+ start_daemon("staticd")
+ while "staticd" in daemons_list:
+ daemons_list.remove("staticd")
+
+ if "snmpd" in daemons_list:
+ # Give zerbra a chance to configure interface addresses that snmpd daemon
+ # may then use.
+ time.sleep(2)
+
+ start_daemon("snmpd")
+ while "snmpd" in daemons_list:
+ daemons_list.remove("snmpd")
+
+ if daemons is None:
+ # Fix Link-Local Addresses on initial startup
+ # Somehow (on Mininet only), Zebra removes the IPv6 Link-Local addresses on start. Fix this
+ _, output, _ = self.cmd_status(
+ "for i in `ls /sys/class/net/` ; do mac=`cat /sys/class/net/$i/address`; echo $i: $mac; [ -z \"$mac\" ] && continue; IFS=':'; set $mac; unset IFS; ip address add dev $i scope link fe80::$(printf %02x $((0x$1 ^ 2)))$2:${3}ff:fe$4:$5$6/64; done",
+ stderr=subprocess.STDOUT,
+ )
+ logger.debug("Set MACs:\n%s", output)
+
+ # Now start all the other daemons
+ for daemon in daemons_list:
+ if self.daemons[daemon] == 0:
+ continue
+ start_daemon(daemon)
+
+ # Check if daemons are running.
+ rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
+ if re.search(r"No such file or directory", rundaemons):
+ return "Daemons are not running"
+
+ # Update the permissions on the log files
+ self.cmd("chown frr:frr -R {}/{}".format(self.logdir, self.name))
+ self.cmd("chmod ug+rwX,o+r -R {}/{}".format(self.logdir, self.name))
+
+ return ""
+
+ def killRouterDaemons(
+ self, daemons, wait=True, assertOnError=True, minErrorVersion="5.1"
+ ):
+ # Kill Running FRR
+ # Daemons(user specified daemon only) using SIGKILL
+ rundaemons = self.cmd("ls -1 /var/run/%s/*.pid" % self.routertype)
+ errors = ""
+ daemonsNotRunning = []
+ if re.search(r"No such file or directory", rundaemons):
+ return errors
+ for daemon in daemons:
+ if rundaemons is not None and daemon in rundaemons:
+ numRunning = 0
+ dmns = rundaemons.split("\n")
+ # Exclude empty string at end of list
+ for d in dmns[:-1]:
+ if re.search(r"%s" % daemon, d):
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(int(daemonpid)):
+ logger.info(
+ "{}: killing {}".format(
+ self.name,
+ os.path.basename(d.rstrip().rsplit(".", 1)[0]),
+ )
+ )
+ self.cmd("kill -9 %s" % daemonpid)
+ if pid_exists(int(daemonpid)):
+ numRunning += 1
+ while wait and numRunning > 0:
+ sleep(
+ 2,
+ "{}: waiting for {} daemon to be stopped".format(
+ self.name, daemon
+ ),
+ )
+
+ # 2nd round of kill if daemons didn't exit
+ for d in dmns[:-1]:
+ if re.search(r"%s" % daemon, d):
+ daemonpid = self.cmd("cat %s" % d.rstrip()).rstrip()
+ if daemonpid.isdigit() and pid_exists(
+ int(daemonpid)
+ ):
+ logger.info(
+ "{}: killing {}".format(
+ self.name,
+ os.path.basename(
+ d.rstrip().rsplit(".", 1)[0]
+ ),
+ )
+ )
+ self.cmd("kill -9 %s" % daemonpid)
+ if daemonpid.isdigit() and not pid_exists(
+ int(daemonpid)
+ ):
+ numRunning -= 1
+ 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
+ else:
+ daemonsNotRunning.append(daemon)
+ if len(daemonsNotRunning) > 0:
+ errors = errors + "Daemons are not running", daemonsNotRunning
+
+ return errors
+
+ def checkRouterCores(self, reportLeaks=True, reportOnce=False):
+ if reportOnce and not self.reportCores:
+ return
+ reportMade = False
+ traces = ""
+ for daemon in self.daemons:
+ if self.daemons[daemon] == 1:
+ # Look for core file
+ corefiles = glob.glob(
+ "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
+ )
+ if len(corefiles) > 0:
+ backtrace = gdb_core(self, daemon, corefiles)
+ traces = (
+ traces
+ + "\n%s: %s crashed. Core file found - Backtrace follows:\n%s"
+ % (self.name, daemon, backtrace)
+ )
+ reportMade = True
+ elif reportLeaks:
+ log = self.getStdErr(daemon)
+ if "memstats" in log:
+ sys.stderr.write(
+ "%s: %s has memory leaks:\n" % (self.name, daemon)
+ )
+ traces = traces + "\n%s: %s has memory leaks:\n" % (
+ self.name,
+ daemon,
+ )
+ log = re.sub("core_handler: ", "", log)
+ log = re.sub(
+ r"(showing active allocations in memory group [a-zA-Z0-9]+)",
+ r"\n ## \1",
+ log,
+ )
+ log = re.sub("memstats: ", " ", log)
+ sys.stderr.write(log)
+ reportMade = True
+ # Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found
+ if checkAddressSanitizerError(
+ self.getStdErr(daemon), self.name, daemon, self.logdir
+ ):
+ sys.stderr.write(
+ "%s: Daemon %s killed by AddressSanitizer" % (self.name, daemon)
+ )
+ traces = traces + "\n%s: Daemon %s killed by AddressSanitizer" % (
+ self.name,
+ daemon,
+ )
+ reportMade = True
+ if reportMade:
+ self.reportCores = False
+ return traces
+
+ def checkRouterRunning(self):
+ "Check if router daemons are running and collect crashinfo they don't run"
+
+ global fatal_error
+
+ daemonsRunning = self.cmd(
+ 'vtysh -c "show logging" | grep "Logging configuration for"'
+ )
+ # Look for AddressSanitizer Errors in vtysh output and append to /tmp/AddressSanitzer.txt if found
+ if checkAddressSanitizerError(daemonsRunning, self.name, "vtysh"):
+ return "%s: vtysh killed by AddressSanitizer" % (self.name)
+
+ for daemon in self.daemons:
+ if daemon == "snmpd":
+ continue
+ if (self.daemons[daemon] == 1) and not (daemon in daemonsRunning):
+ sys.stderr.write("%s: Daemon %s not running\n" % (self.name, daemon))
+ if daemon == "staticd":
+ sys.stderr.write(
+ "You may have a copy of staticd installed but are attempting to test against\n"
+ )
+ sys.stderr.write(
+ "a version of FRR that does not have staticd, please cleanup the install dir\n"
+ )
+
+ # Look for core file
+ corefiles = glob.glob(
+ "{}/{}/{}_core*.dmp".format(self.logdir, self.name, daemon)
+ )
+ if len(corefiles) > 0:
+ 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(
+ "{}/{}/{}.log".format(self.logdir, self.name, daemon)
+ ):
+ log_tail = subprocess.check_output(
+ [
+ "tail -n20 {}/{}/{}.log 2> /dev/null".format(
+ self.logdir, self.name, daemon
+ )
+ ],
+ shell=True,
+ )
+ sys.stderr.write(
+ "\nFrom %s %s %s log file:\n"
+ % (self.routertype, self.name, daemon)
+ )
+ sys.stderr.write("%s\n" % log_tail)
+
+ # Look for AddressSanitizer Errors and append to /tmp/AddressSanitzer.txt if found
+ if checkAddressSanitizerError(
+ self.getStdErr(daemon), self.name, daemon, self.logdir
+ ):
+ return "%s: Daemon %s not running - killed by AddressSanitizer" % (
+ self.name,
+ daemon,
+ )
+
+ return "%s: Daemon %s not running" % (self.name, daemon)
+ return ""
+
+ def checkRouterVersion(self, cmpop, version):
+ """
+ Compares router version using operation `cmpop` with `version`.
+ Valid `cmpop` values:
+ * `>=`: has the same version or greater
+ * '>': has greater version
+ * '=': has the same version
+ * '<': has a lesser version
+ * '<=': has the same version or lesser
+
+ Usage example: router.checkRouterVersion('>', '1.0')
+ """
+
+ # Make sure we have version information first
+ if self.version == None:
+ self.version = self.cmd(
+ os.path.join(self.daemondir, "bgpd") + " -v"
+ ).split()[2]
+ logger.info("{}: running version: {}".format(self.name, self.version))
+
+ rversion = self.version
+ if rversion == None:
+ return False
+
+ result = version_cmp(rversion, version)
+ if cmpop == ">=":
+ return result >= 0
+ if cmpop == ">":
+ return result > 0
+ if cmpop == "=":
+ return result == 0
+ if cmpop == "<":
+ return result < 0
+ if cmpop == "<":
+ return result < 0
+ if cmpop == "<=":
+ return result <= 0
+
+ def get_ipv6_linklocal(self):
+ "Get LinkLocal Addresses from interfaces"
+
+ linklocal = []
+
+ ifaces = self.cmd("ip -6 address")
+ # Fix newlines (make them all the same)
+ ifaces = ("\n".join(ifaces.splitlines()) + "\n").splitlines()
+ interface = ""
+ ll_per_if_count = 0
+ for line in ifaces:
+ m = re.search("[0-9]+: ([^:@]+)[-@a-z0-9:]+ <", line)
+ if m:
+ interface = m.group(1)
+ ll_per_if_count = 0
+ m = re.search(
+ "inet6 (fe80::[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)[/0-9]* scope link",
+ line,
+ )
+ if m:
+ local = m.group(1)
+ ll_per_if_count += 1
+ if ll_per_if_count > 1:
+ linklocal += [["%s-%s" % (interface, ll_per_if_count), local]]
+ else:
+ linklocal += [[interface, local]]
+ return linklocal
+
+ def daemon_available(self, daemon):
+ "Check if specified daemon is installed (and for ldp if kernel supports MPLS)"
+
+ daemon_path = os.path.join(self.daemondir, daemon)
+ if not os.path.isfile(daemon_path):
+ return False
+ if daemon == "ldpd":
+ if version_cmp(platform.release(), "4.5") < 0:
+ return False
+ if not module_present("mpls-router", load=False):
+ return False
+ if not module_present("mpls-iptunnel", load=False):
+ return False
+ return True
+
+ def get_routertype(self):
+ "Return the type of Router (frr)"
+
+ return self.routertype
+
+ def report_memory_leaks(self, filename_prefix, testscript):
+ "Report Memory Leaks to file prefixed with given string"
+
+ leakfound = False
+ filename = filename_prefix + re.sub(r"\.py", "", testscript) + ".txt"
+ for daemon in self.daemons:
+ if self.daemons[daemon] == 1:
+ log = self.getStdErr(daemon)
+ if "memstats" in log:
+ # Found memory leak
+ logger.info(
+ "\nRouter {} {} StdErr Log:\n{}".format(self.name, daemon, log)
+ )
+ if not leakfound:
+ leakfound = True
+ # Check if file already exists
+ fileexists = os.path.isfile(filename)
+ leakfile = open(filename, "a")
+ if not fileexists:
+ # New file - add header
+ leakfile.write(
+ "# Memory Leak Detection for topotest %s\n\n"
+ % testscript
+ )
+ leakfile.write("## Router %s\n" % self.name)
+ leakfile.write("### Process %s\n" % daemon)
+ log = re.sub("core_handler: ", "", log)
+ log = re.sub(
+ r"(showing active allocations in memory group [a-zA-Z0-9]+)",
+ r"\n#### \1\n",
+ log,
+ )
+ log = re.sub("memstats: ", " ", log)
+ leakfile.write(log)
+ leakfile.write("\n")
+ if leakfound:
+ leakfile.close()
+
+
+def frr_unicode(s):
+ """Convert string to unicode, depending on python version"""
+ if sys.version_info[0] > 2:
+ return s
+ else:
+ return unicode(s) # pylint: disable=E0602
+
+
+def is_mapping(o):
+ return isinstance(o, Mapping)
diff --git a/tests/topotests/msdp_mesh_topo1/__init__.py b/tests/topotests/msdp_mesh_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/__init__.py
diff --git a/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..953d90a
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r1/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65000
+ neighbor 10.254.254.2 remote-as 65000
+ neighbor 10.254.254.2 update-source 10.254.254.1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..c1adbd5
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r1/ospfd.conf
@@ -0,0 +1,8 @@
+interface r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r1/pimd.conf b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
new file mode 100644
index 0000000..c2ffed4
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r1/pimd.conf
@@ -0,0 +1,17 @@
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.1
+!
+interface r1-eth0
+ ip pim
+!
+interface r1-eth1
+ ip pim
+ ip igmp
+!
+ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
+ip msdp timers 10 20 3
+ip msdp mesh-group mg-1 source 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.2
+ip msdp mesh-group mg-1 member 10.254.254.3
diff --git a/tests/topotests/msdp_mesh_topo1/r1/zebra.conf b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf
new file mode 100644
index 0000000..42c850f
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r1/zebra.conf
@@ -0,0 +1,11 @@
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.1/32
+!
+interface r1-eth0
+ ip address 192.168.1.2/24
+!
+interface r1-eth1
+ ip address 192.168.10.1/24
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..f442efc
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r2/bgpd.conf
@@ -0,0 +1,10 @@
+router bgp 65000
+ neighbor pg-1 peer-group
+ neighbor pg-1 update-source 10.254.254.1
+ neighbor pg-1 remote-as 65000
+ neighbor 10.254.254.1 peer-group pg-1
+ neighbor 10.254.254.3 peer-group pg-1
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..9e9ac5f
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r2/ospfd.conf
@@ -0,0 +1,13 @@
+interface r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.1.0/24 area 0.0.0.0
+ network 192.168.2.0/24 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r2/pimd.conf b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
new file mode 100644
index 0000000..1719a17
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r2/pimd.conf
@@ -0,0 +1,16 @@
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.2
+!
+interface r2-eth0
+ ip pim
+!
+interface r2-eth1
+ ip pim
+!
+ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
+ip msdp timers 10 20 3
+ip msdp mesh-group mg-1 source 10.254.254.2
+ip msdp mesh-group mg-1 member 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.3
diff --git a/tests/topotests/msdp_mesh_topo1/r2/zebra.conf b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf
new file mode 100644
index 0000000..6b26194
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r2/zebra.conf
@@ -0,0 +1,11 @@
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.2/32
+!
+interface r2-eth0
+ ip address 192.168.1.1/24
+!
+interface r2-eth1
+ ip address 192.168.2.1/24
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf
new file mode 100644
index 0000000..6c3f89a
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r3/bgpd.conf
@@ -0,0 +1,7 @@
+router bgp 65000
+ neighbor 192.168.2.1 remote-as 65000
+ neighbor 192.168.2.1 update-source 10.254.254.3
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..7b7b1ab
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r3/ospfd.conf
@@ -0,0 +1,8 @@
+interface r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 192.168.2.0/24 area 0.0.0.0
+ redistribute connected
+!
diff --git a/tests/topotests/msdp_mesh_topo1/r3/pimd.conf b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
new file mode 100644
index 0000000..2748a55
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r3/pimd.conf
@@ -0,0 +1,17 @@
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.3
+!
+interface r3-eth0
+ ip pim
+!
+interface r3-eth1
+ ip pim
+ ip igmp
+!
+ip pim join-prune-interval 5
+ip pim rp 10.254.254.3
+ip msdp timers 10 20 3
+ip msdp mesh-group mg-1 source 10.254.254.3
+ip msdp mesh-group mg-1 member 10.254.254.1
+ip msdp mesh-group mg-1 member 10.254.254.2
diff --git a/tests/topotests/msdp_mesh_topo1/r3/zebra.conf b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf
new file mode 100644
index 0000000..a8a15f3
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/r3/zebra.conf
@@ -0,0 +1,11 @@
+ip forwarding
+!
+interface lo
+ ip address 10.254.254.3/32
+!
+interface r3-eth0
+ ip address 192.168.2.2/24
+!
+interface r3-eth1
+ ip address 192.168.30.1/24
+!
diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot
new file mode 100644
index 0000000..8792e2c
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.dot
@@ -0,0 +1,88 @@
+## 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="msdp_mesh_topo1";
+
+ # 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,
+ ];
+ h1 [
+ shape=doubleoctagon
+ label="h1",
+ fillcolor="#4f4f4f",
+ style=filled,
+ ];
+ h2 [
+ shape=doubleoctagon
+ label="h2",
+ fillcolor="#4f4f4f",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ shape=oval,
+ label="sw1\n192.168.1.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ shape=oval,
+ label="sw2\n192.168.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ shape=oval,
+ label="sw3\n192.168.10.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ shape=oval,
+ label="sw3\n192.168.30.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- s1 [label="eth0\n.2"];
+ r2 -- s1 [label="eth0\n.1"];
+
+ r2 -- s2 [label="eth1\n.1"];
+ r3 -- s2 [label="eth0\n.2"];
+
+ r1 -- s3 [label="eth1\n.1"];
+ h1 -- s3 [label="eth0\n.2"];
+
+ r3 -- s4 [label="eth1\n.1"];
+ h2 -- s4 [label="eth0\n.2"];
+}
diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png
new file mode 100644
index 0000000..9a15b8b
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.png
Binary files differ
diff --git a/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py
new file mode 100644
index 0000000..138e190
--- /dev/null
+++ b/tests/topotests/msdp_mesh_topo1/test_msdp_mesh_topo1.py
@@ -0,0 +1,242 @@
+#!/usr/bin/env python
+
+#
+# test_msdp_mesh_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (C) 2021 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_msdp_mesh_topo1.py: Test the FRR PIM MSDP mesh groups.
+"""
+
+import os
+import sys
+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
+
+# Required to instantiate the topology builder class.
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.pim import McastTesterHelper
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pimd]
+
+app_helper = McastTesterHelper()
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 3 routers
+ for routern in range(1, 4):
+ 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"])
+
+ # Create stub networks for multicast traffic.
+ tgen.add_host("h1", "192.168.10.2/24", "via 192.168.10.1")
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h1"])
+
+ tgen.add_host("h2", "192.168.30.2/24", "via 192.168.30.1")
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["h2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ 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)
+
+ daemon_file = "{}/{}/ospfd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF, daemon_file)
+
+ daemon_file = "{}/{}/pimd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_PIM, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ app_helper.init(tgen)
+
+
+def test_wait_ospf_convergence():
+ "Wait for OSPF 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=40, 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", "ospf")
+ # Wait for R1 <-> R3 convergence.
+ expect_loopback_route("r1", "ip", "10.254.254.3/32", "ospf")
+
+ # Wait for R2 <-> R1 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.1/32", "ospf")
+ # Wait for R2 <-> R3 convergence.
+ expect_loopback_route("r2", "ip", "10.254.254.3/32", "ospf")
+
+ # Wait for R3 <-> R1 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.1/32", "ospf")
+ # Wait for R3 <-> R2 convergence.
+ expect_loopback_route("r3", "ip", "10.254.254.2/32", "ospf")
+
+
+def test_wait_msdp_convergence():
+ "Wait for MSDP to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test MSDP convergence")
+
+ def expect_msdp_peer(router, peer, sa_count=0):
+ "Expect MSDP peer connection to be established with SA amount."
+ logger.info(
+ "waiting MSDP connection from peer {} on router {}".format(peer, router)
+ )
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip msdp peer json",
+ {peer: {"state": "established", "saCount": sa_count}},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=2)
+ assertmsg = '"{}" MSDP connection failure'.format(router)
+ assert result is None, assertmsg
+
+ mcastaddr = "229.0.1.10"
+ logger.info("Starting helper1")
+ app_helper.run("h1", ["--send=0.7", mcastaddr, "h1-eth0"])
+
+ logger.info("Starting helper2")
+ app_helper.run("h2", [mcastaddr, "h2-eth0"])
+
+ # R1 peers.
+ expect_msdp_peer("r1", "10.254.254.2")
+ expect_msdp_peer("r1", "10.254.254.3")
+
+ # R2 peers.
+ expect_msdp_peer("r2", "10.254.254.1", 1)
+ expect_msdp_peer("r2", "10.254.254.3")
+
+ # R3 peers.
+ expect_msdp_peer("r3", "10.254.254.1", 1)
+ expect_msdp_peer("r3", "10.254.254.2")
+
+
+def test_msdp_sa_configuration():
+ "Expect the multicast traffic SA to be created"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("test MSDP SA")
+
+ def expect_msdp_sa(router, source, group, local, rp, spt_setup):
+ "Expect MSDP SA."
+ logger.info("waiting MSDP SA on router {}".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip msdp sa json",
+ {group: {source: {"local": local, "rp": rp, "sptSetup": spt_setup}}},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"{}" MSDP SA failure'.format(router)
+ assert result is None, assertmsg
+
+ source = "192.168.10.2"
+ group = "229.0.1.10"
+ rp = "10.254.254.1"
+
+ # R1 SA.
+ expect_msdp_sa("r1", source, group, "yes", "-", "-")
+
+ # R2 SA.
+ expect_msdp_sa("r2", source, group, "no", rp, "no")
+
+ # R3 peers.
+ expect_msdp_sa("r3", source, group, "no", rp, "yes")
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ app_helper.cleanup()
+ 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/msdp_topo1/__init__.py b/tests/topotests/msdp_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/msdp_topo1/__init__.py
diff --git a/tests/topotests/msdp_topo1/r1/bgpd.conf b/tests/topotests/msdp_topo1/r1/bgpd.conf
new file mode 100644
index 0000000..01d8ddb
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r1/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.2 remote-as 65002
+ neighbor 192.168.1.2 remote-as 65003
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_topo1/r1/pimd.conf b/tests/topotests/msdp_topo1/r1/pimd.conf
new file mode 100644
index 0000000..5bb268e
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r1/pimd.conf
@@ -0,0 +1,22 @@
+! debug pim
+! debug pim zebra
+!
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.1
+!
+interface r1-eth0
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip pim
+ ip igmp
+!
+ip msdp timers 10 20 3
+ip msdp peer 192.168.0.2 source 192.168.0.1
+ip msdp peer 192.168.1.2 source 192.168.1.1
+ip pim rp 10.254.254.1
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r1/zebra.conf b/tests/topotests/msdp_topo1/r1/zebra.conf
new file mode 100644
index 0000000..fb6eabc
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r1/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+!
+interface r1-eth0
+ ip address 192.168.0.1/24
+!
+interface r1-eth1
+ ip address 192.168.1.1/24
+!
+interface r1-eth2
+ ip address 192.168.10.1/24
+!
+interface lo
+ ip address 10.254.254.1/32
+!
diff --git a/tests/topotests/msdp_topo1/r2/bgpd.conf b/tests/topotests/msdp_topo1/r2/bgpd.conf
new file mode 100644
index 0000000..987bef4
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r2/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.0.1 remote-as 65001
+ neighbor 192.168.2.2 remote-as 65004
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_topo1/r2/pimd.conf b/tests/topotests/msdp_topo1/r2/pimd.conf
new file mode 100644
index 0000000..733bd6f
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r2/pimd.conf
@@ -0,0 +1,18 @@
+! debug pim
+! debug pim zebra
+!
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.2
+!
+interface r2-eth0
+ ip pim
+!
+interface r2-eth1
+ ip pim
+!
+ip msdp timers 10 20 3
+ip msdp peer 192.168.0.1 source 192.168.0.2
+ip msdp peer 192.168.2.2 source 192.168.2.1
+ip pim rp 10.254.254.2
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r2/zebra.conf b/tests/topotests/msdp_topo1/r2/zebra.conf
new file mode 100644
index 0000000..527f7dd
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r2/zebra.conf
@@ -0,0 +1,11 @@
+ip forwarding
+!
+interface r2-eth0
+ ip address 192.168.0.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.1/24
+!
+interface lo
+ ip address 10.254.254.2/32
+!
diff --git a/tests/topotests/msdp_topo1/r3/bgpd.conf b/tests/topotests/msdp_topo1/r3/bgpd.conf
new file mode 100644
index 0000000..02d685b
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r3/bgpd.conf
@@ -0,0 +1,8 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as 65001
+ neighbor 192.168.3.2 remote-as 65004
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
diff --git a/tests/topotests/msdp_topo1/r3/pimd.conf b/tests/topotests/msdp_topo1/r3/pimd.conf
new file mode 100644
index 0000000..47987c0
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r3/pimd.conf
@@ -0,0 +1,18 @@
+! debug pim
+! debug pim zebra
+!
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.3
+!
+interface r3-eth0
+ ip pim
+!
+interface r3-eth1
+ ip pim
+!
+ip msdp timers 10 20 3
+ip msdp peer 192.168.1.1 source 192.168.1.2
+ip msdp peer 192.168.3.2 source 192.168.3.1
+ip pim rp 10.254.254.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r3/zebra.conf b/tests/topotests/msdp_topo1/r3/zebra.conf
new file mode 100644
index 0000000..688e752
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r3/zebra.conf
@@ -0,0 +1,11 @@
+ip forwarding
+!
+interface r3-eth0
+ ip address 192.168.1.2/24
+!
+interface r3-eth1
+ ip address 192.168.3.1/24
+!
+interface lo
+ ip address 10.254.254.3/32
+!
diff --git a/tests/topotests/msdp_topo1/r4/bgpd.conf b/tests/topotests/msdp_topo1/r4/bgpd.conf
new file mode 100644
index 0000000..633e8db
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r4/bgpd.conf
@@ -0,0 +1,9 @@
+router bgp 65004
+ no bgp ebgp-requires-policy
+ neighbor 192.168.2.1 remote-as 65002
+ neighbor 192.168.3.1 remote-as 65003
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+!
+
diff --git a/tests/topotests/msdp_topo1/r4/pimd.conf b/tests/topotests/msdp_topo1/r4/pimd.conf
new file mode 100644
index 0000000..2808591
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r4/pimd.conf
@@ -0,0 +1,22 @@
+! debug pim
+! debug pim zebra
+!
+interface lo
+ ip pim
+ ip pim use-source 10.254.254.4
+!
+interface r4-eth0
+ ip pim
+!
+interface r4-eth1
+ ip pim
+!
+interface r4-eth2
+ ip pim
+ ip igmp
+!
+ip msdp timers 10 20 3
+ip msdp peer 192.168.2.1 source 192.168.2.2
+ip msdp peer 192.168.3.1 source 192.168.3.2
+ip pim rp 10.254.254.4
+ip pim join-prune-interval 5
diff --git a/tests/topotests/msdp_topo1/r4/zebra.conf b/tests/topotests/msdp_topo1/r4/zebra.conf
new file mode 100644
index 0000000..1db8132
--- /dev/null
+++ b/tests/topotests/msdp_topo1/r4/zebra.conf
@@ -0,0 +1,14 @@
+ip forwarding
+!
+interface r4-eth0
+ ip address 192.168.2.2/24
+!
+interface r4-eth1
+ ip address 192.168.3.2/24
+!
+interface r4-eth2
+ ip address 192.168.4.1/24
+!
+interface lo
+ ip address 10.254.254.4/32
+!
diff --git a/tests/topotests/msdp_topo1/test_msdp_topo1.py b/tests/topotests/msdp_topo1/test_msdp_topo1.py
new file mode 100755
index 0000000..46ccd5e
--- /dev/null
+++ b/tests/topotests/msdp_topo1/test_msdp_topo1.py
@@ -0,0 +1,454 @@
+#!/usr/bin/env python
+
+#
+# test_msdp_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 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_msdp_topo1.py: Test the FRR PIM MSDP peer.
+"""
+
+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
+
+# Required to instantiate the topology builder class.
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+from lib.pim import McastTesterHelper
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.pimd]
+
+app_helper = McastTesterHelper()
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # 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["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s4")
+ # switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r4"])
+
+ # Create a host connected and direct at r4:
+ tgen.add_host("h1", "192.168.4.100/24", "via 192.168.4.1")
+ switch.add_link(tgen.gears["h1"])
+
+ # Create a host connected and direct at r1:
+ switch = tgen.add_switch("s6")
+ tgen.add_host("h2", "192.168.10.100/24", "via 192.168.10.1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["h2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ 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)
+
+ daemon_file = "{}/{}/pimd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_PIM, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ app_helper.init(tgen)
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ app_helper.cleanup()
+ tgen.stop_topology()
+
+
+def test_bgp_convergence():
+ "Wait for BGP protocol convergence"
+ 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 = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for R1
+ expect_loopback_route("r1", "ip", "10.254.254.2/32", "bgp")
+ expect_loopback_route("r1", "ip", "10.254.254.3/32", "bgp")
+ expect_loopback_route("r1", "ip", "10.254.254.4/32", "bgp")
+
+ # Wait for R2
+ expect_loopback_route("r2", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r2", "ip", "10.254.254.3/32", "bgp")
+ expect_loopback_route("r2", "ip", "10.254.254.4/32", "bgp")
+
+ # Wait for R3
+ expect_loopback_route("r3", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r3", "ip", "10.254.254.2/32", "bgp")
+ expect_loopback_route("r3", "ip", "10.254.254.4/32", "bgp")
+
+ # Wait for R4
+ expect_loopback_route("r4", "ip", "10.254.254.1/32", "bgp")
+ expect_loopback_route("r4", "ip", "10.254.254.2/32", "bgp")
+ expect_loopback_route("r4", "ip", "10.254.254.3/32", "bgp")
+
+
+def _test_mroute_install():
+ "Test that multicast routes propagated and installed"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Test R1 mroute
+ #
+ expect_1 = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "iif": "r1-eth2",
+ "flags": "SFT",
+ "oil": {
+ "r1-eth0": {"source": "192.168.10.100", "group": "229.1.2.3"},
+ "r1-eth1": None,
+ },
+ }
+ }
+ }
+ # Create a deep copy of `expect_1`.
+ expect_2 = json.loads(json.dumps(expect_1))
+ # The route will be either via R2 or R3.
+ expect_2["229.1.2.3"]["192.168.10.100"]["oil"]["r1-eth0"] = None
+ expect_2["229.1.2.3"]["192.168.10.100"]["oil"]["r1-eth1"] = {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ }
+
+ def test_r1_mroute():
+ "Test r1 multicast routing table function"
+ out = tgen.gears["r1"].vtysh_cmd("show ip mroute json", isjson=True)
+ if topotest.json_cmp(out, expect_1) is None:
+ return None
+ return topotest.json_cmp(out, expect_2)
+
+ logger.info("Waiting for R1 multicast routes")
+ _, val = topotest.run_and_expect(test_r1_mroute, None, count=55, wait=2)
+ assert val is None, "multicast route convergence failure"
+
+ #
+ # Test routers 2 and 3.
+ #
+ # NOTE: only one of the paths will get the multicast route.
+ #
+ expect_r2 = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "iif": "r2-eth0",
+ "flags": "S",
+ "oil": {
+ "r2-eth1": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ }
+ },
+ }
+ }
+ }
+ expect_r3 = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "iif": "r3-eth0",
+ "flags": "S",
+ "oil": {
+ "r3-eth1": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ }
+ },
+ }
+ }
+ }
+
+ def test_r2_r3_mroute():
+ "Test r2/r3 multicast routing table function"
+ r2_out = tgen.gears["r2"].vtysh_cmd("show ip mroute json", isjson=True)
+ r3_out = tgen.gears["r3"].vtysh_cmd("show ip mroute json", isjson=True)
+
+ if topotest.json_cmp(r2_out, expect_r2) is not None:
+ return topotest.json_cmp(r3_out, expect_r3)
+
+ return topotest.json_cmp(r2_out, expect_r2)
+
+ logger.info("Waiting for R2 and R3 multicast routes")
+ _, val = topotest.run_and_expect(test_r2_r3_mroute, None, count=55, wait=2)
+ assert val is None, "multicast route convergence failure"
+
+ #
+ # Test router 4
+ #
+ expect_4 = {
+ "229.1.2.3": {
+ "*": {
+ "iif": "lo",
+ "flags": "SC",
+ "oil": {
+ "pimreg": {
+ "source": "*",
+ "group": "229.1.2.3",
+ "inboundInterface": "lo",
+ "outboundInterface": "pimreg",
+ },
+ "r4-eth2": {
+ "source": "*",
+ "group": "229.1.2.3",
+ "inboundInterface": "lo",
+ "outboundInterface": "r4-eth2",
+ },
+ },
+ },
+ "192.168.10.100": {
+ "iif": "r4-eth0",
+ "flags": "ST",
+ "oil": {
+ "r4-eth2": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ "inboundInterface": "r4-eth0",
+ "outboundInterface": "r4-eth2",
+ }
+ },
+ },
+ }
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears["r4"],
+ "show ip mroute json",
+ expect_4,
+ )
+ logger.info("Waiting for R4 multicast routes")
+ _, val = topotest.run_and_expect(test_func, None, count=55, wait=2)
+ assert val is None, "multicast route convergence failure"
+
+
+def test_mroute_install():
+ tgen = get_topogen()
+ # pytest.skip("FOO")
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Starting helper1")
+ mcastaddr = "229.1.2.3"
+ app_helper.run("h1", [mcastaddr, "h1-eth0"])
+
+ logger.info("Starting helper2")
+ app_helper.run("h2", ["--send=0.7", mcastaddr, "h2-eth0"])
+
+ _test_mroute_install()
+
+
+def test_msdp():
+ """
+ Test MSDP convergence.
+
+ MSDP non meshed groups must propagate the whole SA database (not just
+ their own) to all peers because not all peers talk with each other.
+
+ This setup leads to a potential loop that can be prevented by checking
+ the route's first AS in AS path: it must match the remote eBGP AS number.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r1_expect = {
+ "192.168.0.2": {
+ "peer": "192.168.0.2",
+ "local": "192.168.0.1",
+ "state": "established",
+ },
+ "192.168.1.2": {
+ "peer": "192.168.1.2",
+ "local": "192.168.1.1",
+ "state": "established",
+ },
+ }
+ r1_sa_expect = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ "rp": "-",
+ "local": "yes",
+ "sptSetup": "-",
+ }
+ }
+ }
+ r2_expect = {
+ "192.168.0.1": {
+ "peer": "192.168.0.1",
+ "local": "192.168.0.2",
+ "state": "established",
+ },
+ "192.168.2.2": {
+ "peer": "192.168.2.2",
+ "local": "192.168.2.1",
+ "state": "established",
+ },
+ }
+ # Only R2 or R3 will get this SA.
+ r2_r3_sa_expect = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ "rp": "192.168.1.1",
+ "local": "no",
+ "sptSetup": "no",
+ }
+ }
+ }
+ r3_expect = {
+ "192.168.1.1": {
+ "peer": "192.168.1.1",
+ "local": "192.168.1.2",
+ "state": "established",
+ },
+ # "192.169.3.2": {
+ # "peer": "192.168.3.2",
+ # "local": "192.168.3.1",
+ # "state": "established"
+ # }
+ }
+ r4_expect = {
+ "192.168.2.1": {
+ "peer": "192.168.2.1",
+ "local": "192.168.2.2",
+ "state": "established",
+ },
+ # "192.168.3.1": {
+ # "peer": "192.168.3.1",
+ # "local": "192.168.3.2",
+ # "state": "established"
+ # }
+ }
+ r4_sa_expect = {
+ "229.1.2.3": {
+ "192.168.10.100": {
+ "source": "192.168.10.100",
+ "group": "229.1.2.3",
+ "rp": "192.168.1.1",
+ "local": "no",
+ "sptSetup": "yes",
+ }
+ }
+ }
+
+ for router in [
+ ("r1", r1_expect, r1_sa_expect),
+ ("r2", r2_expect, r2_r3_sa_expect),
+ ("r3", r3_expect, r2_r3_sa_expect),
+ ("r4", r4_expect, r4_sa_expect),
+ ]:
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router[0]],
+ "show ip msdp peer json",
+ router[1],
+ )
+ logger.info("Waiting for {} msdp peer data".format(router[0]))
+ _, val = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert val is None, "multicast route convergence failure"
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router[0]],
+ "show ip msdp sa json",
+ router[2],
+ )
+ logger.info("Waiting for {} msdp SA data".format(router[0]))
+ _, val = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assert val is None, "multicast route convergence failure"
+
+
+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/multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json b/tests/topotests/multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json
new file mode 100644
index 0000000..14cb0be
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo1/mcast_pim_bsmp_01.json
@@ -0,0 +1,238 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "b1": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}
+ },
+ "bsm": {
+ "bsr_packets": {
+ "packet1" : {
+ "data": "01005e00000d005056961165080045c000aa5af500000167372a46000001e000000d2400f5ce165b000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e1010101010100000100050606050096000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "225.1.1.1/32": ["5.6.6.5/32"],
+ "225.200.100.100/32": ["210.210.210.210/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+
+ },
+ "Desc" : "Packet with 3 group range - rp prio different"
+ },
+ "packet2" : {
+ "data": "01005e00000d005056961165080045c0009420f400000167714146000001e000000d24000b3b164a000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e20101010101000001000909090900000000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"]
+ },
+ "Desc" : "Packet 1 with hold time 0 for 226.1.1.1/32"
+ },
+ "packet3" : {
+ "data": "01005e00000d005056961165080045c000944d0000000167453546000001e000000d2400e52b17c3000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "BSR Prio - TC 4"
+ },
+ "packet4" : {
+ "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "225.1.1.1/32": ["9.9.9.9/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 5"
+ },
+ "packet5" : {
+ "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 5, 225.1.1.1 with hold time 0"
+ },
+ "packet6" : {
+ "data": "01005e00000d005056961165080045c0008a795e0000016718e146000001e000000d24006cc509d5000001004600000101000018e10101000707000001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 6,High prio rp removed on 225.1.1.0/24"
+ },
+ "packet7" : {
+ "data": "01005e00000d005056961165080045c0007e6ebb00000167239046000001e000000d2400090810b3000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a00966400",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"]
+ },
+ "Desc" : "TC - 8, rps with same priority"
+ },
+
+ "packet8" : {
+ "data": "01005e00000d005056b76687080045c000383cdf0000016755b246000001e000000d24008ad51a9f000001004600000101000020e1c86464010100000100d2d2d2d200960000",
+ "group": "225.200.100.100/32",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ },
+ "Desc" : "TC - 30, grp add with all octet"
+ },
+
+ "packet9" : {
+ "data": "01005e00000d005056b76687080045c000387b8600000167170b46000001e000000d2400c6282245000001000101020701000020e1c86464010100000100d2d2d2d200960000",
+ "group": "225.200.100.100/32",
+ "candidate_rp": "210.210.210.210/32",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "1.1.2.7/32",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ },
+ "Desc" : "TC -29, BSM with preferred ip"
+ }
+
+ }
+ }
+ },
+
+ "b2": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}
+ },
+ "bsm": {
+ "bsr_packets": {
+ "packet1" : {
+ "data": "01005e00000d005056b70489080045c0003865db0000016731b641000001e000000d2400659c0c6f000001004100000101000018e10101000101000001002121212100960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "65.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["33.33.33.33/32"],
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ }
+ },
+ "packet2" : {
+ "data": "01005e00000d005056b70489080045c00038663000000167316141000001e000000d24006dce0433000a01004100000101000018e10101000101000001002121212100960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "65.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["33.33.33.33/32"]
+ }
+ },
+
+ "packet3" : {
+ "data": "01005e00000d005056b76687080045c00038f5c800000167a1c841000001e000000d2400c6621a10000001000a02010101000020e1c86464010100000100d2d2d2d200960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "10.2.1.1/32",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ }
+ }
+
+ }
+ }
+ },
+
+ "f1": {
+ "links": {
+ "b1": {"ipv4": "auto", "pim": "enable"},
+ "b2": {"ipv4": "auto", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "s1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "i1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "l1": {
+ "links": {
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-r1-eth1" :{
+ "igmp":{
+ "version": "2"
+ }
+ }
+ }
+ }
+ },
+ "s1": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "r1": {
+ "links": {
+ "l1": {"ipv4": "auto", "pim": "disable"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
new file mode 100644
index 0000000..bcf8e5b
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo1/test_mcast_pim_bsmp_01.py
@@ -0,0 +1,1787 @@
+#!/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 PIM BSM processing basic functionality:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Tests covered in this suite
+1. Verify FRR router select higher IP BSR , when 2 BSR present in the network
+2. Verify BSR and RP updated correctly after configuring as black hole address
+3.1 Verify when new router added to the topology, FRR node will send
+ unicast BSM to new router
+3.2 Verify if no forwarding bit is set , FRR is not forwarding the
+ BSM to other PIM nbrs
+3.3 Verify multicast BSM is sent to new router when unicast BSM is disabled
+4.1 Verify BSM arrived on non bsm capable interface is dropped and
+ not processed
+4.2 Verify group to RP info updated correctly in FRR node, after shut and
+ no-shut of BSM enable interfaces
+5. Verify static RP is preferred over BSR
+6.1 Verify adding/deleting the group to rp mapping and RP priority
+ multiple times
+6.2 Verify RP and (*,G) detail after PIM process restart on FRR node
+7.1 Verify BSM timeout on FRR1
+7.2 Verify RP state in FRR1 after Bootstrap timer expiry
+8.1 Verify upstream interfaces(IIF) and join state are updated properly
+ after BSM received for FRR
+8.2 Verify IIF and OIL in "show ip pim state" updated properly after
+ BSM received
+"""
+
+import os
+import sys
+import time
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ addKernelRoute,
+ create_static_routes,
+ stop_router,
+ start_router,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ reset_config_on_routers,
+ do_countdown,
+ apply_raw_config,
+ run_frr_cmd,
+ required_linux_kernel_version,
+ topo_daemons,
+ verify_rib,
+)
+
+from lib.pim import (
+ create_pim_config,
+ add_rp_interfaces_and_pim_config,
+ reconfig_interfaces,
+ scapy_send_bsr_raw_packet,
+ find_rp_from_bsrp_info,
+ verify_pim_grp_rp_source,
+ verify_pim_bsr,
+ verify_mroutes,
+ verify_join_state_and_timer,
+ verify_pim_state,
+ verify_upstream_iif,
+ verify_igmp_groups,
+ verify_pim_upstream_rpf,
+ enable_disable_pim_unicast_bsm,
+ enable_disable_pim_bsm,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ get_pim_interface_traffic,
+ McastTesterHelper,
+ verify_pim_neighbors,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+TOPOLOGY = """
+
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+"""
+# Global variables
+NEXT_HOP1 = "70.0.0.1"
+NEXT_HOP2 = "65.0.0.1"
+BSR_IP_1 = "1.1.2.7"
+BSR_IP_2 = "10.2.1.1"
+BSR1_ADDR = "1.1.2.7/32"
+BSR2_ADDR = "10.2.1.1/32"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/mcast_pim_bsmp_01.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def clear_bsrp_data(tgen, topo):
+
+ """
+ clear bsm databas after test"
+ Parameters
+ ----------
+ * `tgen`: topogen object
+
+ Usage
+ -----
+ result = clear_bsrp_data(tgen, topo)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ for dut in tgen.routers():
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: clear_bsrp_data")
+
+ run_frr_cmd(rnode, "clear ip pim bsr-data")
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, packet):
+ """
+ API to do required configuration to send and receive BSR packet
+ """
+
+ # Re-configure interfaces as per BSR packet
+ result = reconfig_interfaces(tgen, topo, bsr, fhr, packet)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Create static routes
+ if "bsr" in topo["routers"][bsr]["bsm"]["bsr_packets"][packet]:
+ bsr_route = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["bsr"]
+ next_hop = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["src_ip"].split(
+ "/"
+ )[0]
+ next_hop_rp = topo["routers"][fhr]["links"][rp]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0]
+
+ # Add static routes
+ input_dict = {
+ rp: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_rp}]},
+ lhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_lhr}]},
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ for dut, _nexthop in zip([rp, lhr], [next_hop_rp, next_hop_lhr]):
+ input_routes = {dut: input_dict[dut]}
+ result = verify_rib(
+ tgen, "ipv4", dut, input_routes, _nexthop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # RP Mapping
+ rp_mapping = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["rp_mapping"]
+
+ # Add interfaces in RP for all the RPs
+ result = add_rp_interfaces_and_pim_config(tgen, topo, "lo", rp, rp_mapping)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Add kernel routes to sender and receiver
+ for group, rp_list in rp_mapping.items():
+ mask = group.split("/")[1]
+ if int(mask) == 32:
+ group = group.split("/")[0]
+
+ # Add static routes for RPs in FHR and LHR
+ next_hop_fhr = topo["routers"][rp]["links"][fhr]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0]
+ input_dict = {
+ fhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_fhr}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(
+ tgen, "ipv4", fhr, input_dict, next_hop_fhr, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ lhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_lhr}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(
+ tgen, "ipv4", lhr, input_dict, next_hop_lhr, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_BSR_higher_prefer_ip_p0(request):
+ """
+ Verify FRR router select higher IP BSR , when 2 BSR present in the network
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("pre-configure BSM packet")
+ step("Configure cisco-1 as BSR1 1.1.2.7")
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ step("Configure cisco-1 as BSR1 10.2.1.1")
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ step("configuring loopback address of b1 and b2 as BSR")
+ intf_lo_addr_b1 = topo["routers"]["b1"]["links"]["lo"]["ipv4"]
+ intf_lo_addr_b2 = topo["routers"]["b2"]["links"]["lo"]["ipv4"]
+
+ raw_config = {
+ "b1": {
+ "raw_config": [
+ "interface lo",
+ "no ip address {}".format(intf_lo_addr_b1),
+ "ip address {}".format(BSR1_ADDR),
+ "ip pim",
+ ]
+ },
+ "b2": {
+ "raw_config": [
+ "interface lo",
+ "no ip address {}".format(intf_lo_addr_b2),
+ "ip address {}".format(BSR2_ADDR),
+ "ip pim",
+ ]
+ },
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.200.100.100"
+ step("configuring static routes for both the BSR")
+
+ next_hop_rp = topo["routers"]["f1"]["links"]["i1"]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "f1": {
+ "static_routes": [
+ {"network": BSR1_ADDR, "next_hop": NEXT_HOP1},
+ {"network": BSR2_ADDR, "next_hop": NEXT_HOP2},
+ ]
+ },
+ "i1": {
+ "static_routes": [
+ {"network": BSR1_ADDR, "next_hop": next_hop_rp},
+ {"network": BSR2_ADDR, "next_hop": next_hop_rp},
+ ]
+ },
+ "l1": {
+ "static_routes": [
+ {"network": BSR1_ADDR, "next_hop": next_hop_lhr},
+ {"network": BSR2_ADDR, "next_hop": next_hop_lhr},
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ for dut, _nexthop in zip(["i1", "l1"], [next_hop_rp, next_hop_lhr]):
+ input_routes = {dut: input_dict[dut]}
+ result = verify_rib(
+ tgen, "ipv4", dut, input_routes, _nexthop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ for bsr_add, next_hop in zip([BSR1_ADDR, BSR2_ADDR], [NEXT_HOP1, NEXT_HOP2]):
+ input_routes = {
+ "f1": {"static_routes": [{"network": bsr_add, "next_hop": next_hop}]}
+ }
+ result = verify_rib(
+ tgen, "ipv4", "f1", input_routes, next_hop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+ do_countdown(5)
+
+ dut = "l1"
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"]
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Send BSR packet from b2 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ step("Verify if b2 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_2, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut higher prefer BSR2 link f1 to b2")
+
+ f1_b2_eth1 = topo["routers"]["f1"]["links"]["b2"]["interface"]
+ shutdown_bringup_interface(tgen, "f1", "f1-b2-eth1", False)
+
+ step("clearing bsr to timeout old BSR")
+ clear_bsrp_data(tgen, topo)
+
+ step("Send BSR packet from b1 and b2 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("sleeping for 3 sec to leran new packet")
+ do_countdown(3)
+ step("verify BSR1 has become preferred RP")
+ dut = "l1"
+
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("NoShut higher prefer BSR2 link f1 to b2")
+ step("sleeping for 3 min to leran new packet")
+ do_countdown(3)
+ f1_b2_eth1 = topo["routers"]["f1"]["links"]["b2"]["interface"]
+ shutdown_bringup_interface(tgen, "f1", "f1-b2-eth1", True)
+ step("verify BSR2 has become preferred RP")
+ dut = "l1"
+
+ step("Send BSR packet from b1 and b2 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet3")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify if b2 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_2)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_2, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_BSR_CRP_with_blackhole_address_p1(request):
+ """
+ Verify BSR and RP updated correctly after configuring as black hole address
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("pre-configure BSM packet")
+ step("Configure cisco-1 as BSR1 1.1.2.7")
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("configuring loopback address of b1 and b2 as BSR")
+ intf_lo_addr_b1 = topo["routers"]["b1"]["links"]["lo"]["ipv4"]
+
+ raw_config = {
+ "b1": {
+ "raw_config": [
+ "interface lo",
+ "no ip address {}".format(intf_lo_addr_b1),
+ "ip address {}".format(BSR1_ADDR),
+ "ip pim",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.200.100.100"
+ step("configuring static routes for both the BSR")
+
+ next_hop_rp = topo["routers"]["f1"]["links"]["i1"]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0]
+ next_hop_fhr = topo["routers"]["i1"]["links"]["f1"]["ipv4"].split("/")[0]
+ CRP = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["candidate_rp"]
+
+ input_dict = {
+ "i1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_rp}]},
+ "l1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": next_hop_lhr}]},
+ "f1": {
+ "static_routes": [
+ {"network": CRP, "next_hop": next_hop_fhr, "delete": True}
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ for dut, _nexthop in zip(["i1", "l1"], [next_hop_rp, next_hop_lhr]):
+ input_routes = {dut: input_dict[dut]}
+ result = verify_rib(
+ tgen, "ipv4", dut, input_routes, _nexthop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_routes = {
+ "f1": {"static_routes": [{"network": CRP, "next_hop": next_hop_fhr}]}
+ }
+ result = verify_rib(
+ tgen, "ipv4", "f1", input_routes, protocol="static", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Route is still present \n Error {}".format(
+ tc_name, result
+ )
+
+ # Use scapy to send pre-defined packet from senser to receiver
+
+ group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"]
+ step("waiting for BSR to timeout before configuring blackhole route")
+ clear_bsrp_data(tgen, topo)
+
+ step("Configure black-hole address for BSR and candidate RP")
+ input_dict = {
+ "f1": {
+ "static_routes": [{"network": [BSR1_ADDR, CRP], "next_hop": "blackhole"}]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(tgen, "ipv4", "f1", input_dict, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ intf_f1_i1 = topo["routers"]["f1"]["links"]["i1"]["interface"]
+ step("Verify bsm transit count is not increamented" "show ip pim interface traffic")
+ state_dict = {"f1": {intf_f1_i1: ["bsmTx"]}}
+
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Sending BSR after Configure black hole address for BSR and candidate RP")
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify if b1 chosen as BSR in l1")
+ result = verify_pim_bsr(tgen, topo, "l1", BSR_IP_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "b1 is not chosen as BSR in l1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is not True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove black-hole address for BSR and candidate RP")
+ input_dict = {
+ "f1": {
+ "static_routes": [
+ {"network": [BSR1_ADDR, CRP], "next_hop": "blackhole", "delete": True},
+ {"network": BSR1_ADDR, "next_hop": NEXT_HOP1},
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ input_dict = {
+ "f1": {"static_routes": [{"network": BSR1_ADDR, "next_hop": NEXT_HOP1}]}
+ }
+ result = verify_rib(tgen, "ipv4", "f1", input_dict, NEXT_HOP1, protocol="static")
+ assert result is True, "Testcase {} : Failed \n Error {}".format(tc_name, result)
+
+ input_dict = {
+ "f1": {
+ "static_routes": [
+ {"network": [BSR1_ADDR, CRP], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, "ipv4", "f1", input_dict, protocol="static", expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Routes:[{}, {}] are still present \n Error {}".format(
+ tc_name, BSR1_ADDR, CRP, result
+ )
+ )
+
+ step("Sending BSR after removing black-hole address for BSR and candidate RP")
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet9")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", BSR_IP_1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"]
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, BSR_IP_1, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_new_router_fwd_p0(request):
+ """
+ 1. Verify when new router added to the topology, FRR node will send
+ unicast BSM to new router
+ 2. Verify if no forwarding bit is set , FRR is not forwarding the
+ BSM to other PIM nbrs
+ 3. Verify multicast BSM is sent to new router when unicast BSM is disabled
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in i1
+ step("Verify if b1 chosen as BSR in i1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify ip mroute
+ iif = "l1-i1-eth0"
+ src_addr = "*"
+ oil = "l1-r1-eth1"
+
+ step("Verify mroute populated on l1")
+ result = verify_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Reload i1 and l1
+ step("Reloading i1 and l1. Stop both. bring up l1 and then i1")
+ stop_router(tgen, "i1")
+ start_router(tgen, "i1")
+ stop_router(tgen, "l1")
+ start_router(tgen, "l1")
+
+ # Verify bsr state in i1
+ step("Verify BSR in i1 after restart while no new bsm sent from b1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in l1
+ step("Verify no BSR in l1 as i1 would not forward the no-forward bsm")
+ result = verify_pim_bsr(tgen, topo, "l1", bsr_ip, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "BSR data is present after no-forward bsm also \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ # unconfigure unicast bsm on f1-i1-eth2
+ step("unconfigure unicast bsm on f1-i1-eth2, will forward with only mcast")
+ enable_disable_pim_unicast_bsm(tgen, "f1", "f1-i1-eth2", enable=False)
+
+ # Reboot i1 to check if still bsm received with multicast address
+ step("Reboot i1 to check if still bsm received with multicast address")
+ stop_router(tgen, "i1")
+ start_router(tgen, "i1")
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify again if BSR is installed from bsm forwarded by f1
+ step("Verify again if BSR is installed from bsm forwarded by f1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send another BSM packet from b1 which will reach l1(LHR)")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ do_countdown(5)
+
+ step("Verify again if BSR is installed from bsm forwarded by i1")
+ result = verify_pim_bsr(tgen, topo, "l1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify ip mroute populated again
+ step("Verify mroute again on l1 (lhr)")
+ result = verify_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_int_bsm_config_p1(request):
+ """
+ 1. Verify BSM arrived on non bsm capable interface is dropped and
+ not processed
+ 2. Verify group to RP info updated correctly in FRR node, after shut and
+ no-shut of BSM enable interfaces
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSM packet from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in i1
+ step("Verify if b1 is chosen as BSR in i1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # check if mroute installed
+ step("check if mroute installed in i1")
+ iif = "lo"
+ src_addr = "*"
+ oil = "i1-l1-eth1"
+
+ result = verify_mroutes(tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # wait till bsm rp age out
+ step("wait till bsm rp age out")
+ clear_bsrp_data(tgen, topo)
+
+ # check if mroute uninstalled because of rp age out
+ step("check if mroute uninstalled because of rp age out in i1")
+ result = verify_mroutes(
+ tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ # unconfigure bsm processing on f1 on f1-i1-eth2
+ step("unconfigure bsm processing on f1 in f1-i1-eth2, will drop bsm")
+ result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=False)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSM packet from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify bsr state in i1
+ step("Verify if b1 is not chosen as BSR in i1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "b1 is chosen as BSR in i1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ # check if mroute still not installed because of rp not available
+ step("check if mroute still not installed because of rp not available")
+ result = verify_mroutes(
+ tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroute installed but rp not available \n Error: {}".format(tc_name, result)
+ )
+
+ # configure bsm processing on i1 on f1-i1-eth2
+ step("configure bsm processing on f1 in f1-i1-eth2, will accept bsm")
+ result = enable_disable_pim_bsm(tgen, "f1", "f1-i1-eth2", enable=True)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSM packet again from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify again if BSR is installed from bsm forwarded by f1
+ step("Verify again if BSR is installed from bsm forwarded by f1")
+ result = verify_pim_bsr(tgen, topo, "i1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # verify ip mroute populated
+ step("Verify ip mroute")
+ result = verify_mroutes(tgen, "i1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Shut/No shut the bsm rpf interface and check mroute on lhr(l1)
+ step("Shut/No shut the bsm rpf interface and check mroute on lhr(l1)")
+ intf = "l1-i1-eth0"
+ shutdown_bringup_interface(tgen, "l1", intf, False)
+ shutdown_bringup_interface(tgen, "l1", intf, True)
+
+ iif = "l1-i1-eth0"
+ oil = "l1-r1-eth1"
+
+ result = verify_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_static_rp_override_p1(request):
+ """
+ Verify static RP is preferred over BSR
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check igmp groups
+ step("Verify IGMP groups in LHR")
+ dut = "l1"
+ intf = "l1-r1-eth1"
+ result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ group = "225.1.1.1/32"
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify that BS RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ iif = "l1-i1-eth0"
+ # Verify upstream rpf for 225.1.1.1 is chosen as rp1
+ step("Verify upstream rpf for 225.1.1.1 is chosen as bsrp")
+ result = verify_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Configure a static rp for the group 225.1.1.1/32
+ step("Configure a static rp 33.33.33.33 for the group 225.1.1.1/32 in l1")
+ input_dict = {
+ "l1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "33.33.33.33",
+ "group_addr_range": ["225.1.1.1/32"],
+ }
+ ]
+ }
+ }
+ }
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verify that static rp is configured over bsrp
+ static_rp = "33.33.33.33"
+ step("Verify that Static RP in LHR in l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "Static", static_rp)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify if upstream also reflects the static rp
+ step("Verify upstream rpf for 225.1.1.1 is chosen as static in l1")
+ result = verify_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, static_rp)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # delete static rp for the group 225.1.1.1/32
+ step("Delete static rp 33.33.33.33 for the group 225.1.1.1/32 in l1")
+ input_dict = {
+ "l1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "33.33.33.33",
+ "group_addr_range": ["225.1.1.1/32"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Verify if bsrp is installed back for the group 225.1.1.1/32
+ step("Verify that BS RP in installed in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify upstream rpf for 225.1.1.1 is chosen as bsrp
+ step("Verify upstream rpf for 225.1.1.1 is chosen as bsrp in l1")
+ result = verify_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_bsmp_stress_add_del_restart_p2(request):
+ """
+ 1. Verify adding/deleting the group to rp mapping and RP priority
+ multiple times
+ 2. Verify RP and (*,G) detail after PIM process restart on FRR node
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 is chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ group = "225.1.1.0/24"
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp1[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24
+ step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ logger.info("RP old: %s RP2 new: %s", rp1[group], rp2[group])
+
+ # Verify is the rp is different now
+ assert rp1[group] != rp2[group], "Testcase {} :Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ rp_add1 = rp1[group]
+ rp_add2 = rp2[group]
+
+ # Verify if that rp is installed
+ step("Verify new RP in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Change rp priority in the bsm and send multiple times")
+
+ for i in range(4):
+ # Send BSR pkt from b1 after putting back high prio rp for 225.1.1.0/24
+ step("Send BSM from b1 to FHR put back high prio rp for 225.1.1.0/24")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR")
+ rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp2 is not {}, "Testcase {} :Failed \n Error : RP not Found".format(
+ tc_name
+ )
+
+ # Verify is the rp is different now
+ step("Verify now old RP is elected again")
+ assert (
+ rp_add1 == rp2[group]
+ ), "Testcase {} :Failed \n Error : rp expected {} rp received {}".format(
+ tc_name, rp_add1, rp2[group]
+ )
+
+ # Verify if that rp is installed
+ step("Verify old RP in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add1)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24
+ step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify if that rp is installed
+ step("Verify new RP(rp2) in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Restart pimd
+ step("Restarting pimd in LHR")
+ kill_router_daemons(tgen, "l1", ["pimd"])
+ start_router_daemons(tgen, "l1", ["pimd"])
+ logger.info("Restarting done")
+
+ # Verify if that rp is installed
+ step("Verify old RP in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send IGMP join to LHR
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ do_countdown(5)
+
+ # VErify mroute created after pimd restart
+ step("VErify mroute created after pimd restart")
+ iif = "l1-i1-eth0"
+ src_addr = "*"
+ oil = "l1-r1-eth1"
+ result = verify_mroutes(tgen, "l1", src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_BSM_timeout_p0(request):
+ """
+ Verify BSM timeout on FRR1
+ Verify RP state in FRR1 after Bootstrap timer expiry
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("send BSR packet from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Send IGMP join for group 225.1.1.1 from receiver
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify bsr state in FHR f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify ip mroute in LHR
+ step(" Verify ip mroute in LHR l1")
+ dut = "l1"
+ iif = "l1-i1-eth0"
+ src_addr = "*"
+ oil = "l1-r1-eth1"
+ result = verify_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify join state and join timer
+ step("Verify join state and join timer in lhr l1")
+ result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify upstream IIF interface
+ step("Verify upstream IIF interface in LHR l1")
+ result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify RP mapping
+ dut = "l1"
+ group = "225.1.1.1/32"
+ step("Verify RP mapping in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp != {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ logger.info("Waiting for 130 secs to check BSR timeout")
+ clear_bsrp_data(tgen, topo)
+
+ # Verify if bsr has aged out
+ step("Verify if bsr has aged out in f1")
+ no_bsr_ip = "0.0.0.0"
+ result = verify_pim_bsr(tgen, topo, "f1", no_bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = verify_pim_grp_rp_source(
+ tgen, topo, "f1", group, rp_source="BSR", expected=False
+ )
+
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "bsr has not aged out in f1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verify RP mapping removed after hold timer expires
+ group = "225.1.1.1/32"
+ step("Verify RP mapping removed after hold timer expires in l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp == {}, "Testcase {} :Failed \n Error : RP found when not expected".format(
+ tc_name
+ )
+
+ # Verify iif is unknown after RP timeout
+ step("Verify iif is unknown after RP timeout in l1")
+ iif = "Unknown"
+ result = verify_upstream_iif(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify join state and join timer
+ step("Verify join state and join timer in l1")
+ iif = "l1-i1-eth0"
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "join state is up and join timer is running in l1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ # Verify ip mroute is not installed
+ step("Verify mroute not installed in l1")
+ result = verify_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_iif_join_state_p0(request):
+ """
+ 1. Verify upstream interfaces(IIF) and join state are updated properly
+ after BSM received for FRR
+ 2. Verify IIF and OIL in "show ip pim state" updated properly after
+ BSM received
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check igmp groups
+ step("Verify IGMP groups in LHR l1")
+ dut = "l1"
+ intf = "l1-r1-eth1"
+ result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ group = "225.1.1.1/32"
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify join state and join timer
+ step("Verify join state and join timer l1")
+ iif = "l1-i1-eth0"
+ src_addr = "*"
+ result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify upstream IIF interface
+ step("Verify upstream IIF interface l1")
+ result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify IIF/OIL in pim state
+ oil = "l1-r1-eth1"
+ result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify ip mroute
+ src_addr = "*"
+ step("Verify ip mroute in l1")
+ result = verify_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Make RP unreachanble in LHR
+ step("Make RP unreachanble in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ next_hop_lhr = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0]
+
+ rp_ip = rp[group] + "/32"
+ input_dict = {
+ "l1": {
+ "static_routes": [
+ {"network": rp_ip, "next_hop": next_hop_lhr, "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(
+ tgen, "ipv4", "l1", input_dict, protocol="static", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "Routes:{} are still present \n Error {}".format(
+ tc_name, rp_ip, result
+ )
+
+ # Check RP unreachable
+ step("Check RP unreachability")
+ iif = "Unknown"
+ result = verify_upstream_iif(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify that it is not installed
+ step("Verify that it is not installed")
+ iif = "<iif?>"
+ result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS, installed_fl=0)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify mroute not installed
+ step("Verify mroute not installed")
+ result = verify_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Add back route for RP to make it reachable
+ step("Add back route for RP to make it reachable")
+ input_dict = {
+ "l1": {
+ "static_routes": [
+ {
+ "network": rp_ip,
+ "next_hop": next_hop_lhr,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(tgen, "ipv4", "l1", input_dict, next_hop_lhr, protocol="static")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify that (*,G) installed in mroute again
+ iif = "l1-i1-eth0"
+ result = verify_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json b/tests/topotests/multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json
new file mode 100644
index 0000000..14cb0be
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo2/mcast_pim_bsmp_02.json
@@ -0,0 +1,238 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "b1": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}
+ },
+ "bsm": {
+ "bsr_packets": {
+ "packet1" : {
+ "data": "01005e00000d005056961165080045c000aa5af500000167372a46000001e000000d2400f5ce165b000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e1010101010100000100050606050096000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "225.1.1.1/32": ["5.6.6.5/32"],
+ "225.200.100.100/32": ["210.210.210.210/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+
+ },
+ "Desc" : "Packet with 3 group range - rp prio different"
+ },
+ "packet2" : {
+ "data": "01005e00000d005056961165080045c0009420f400000167714146000001e000000d24000b3b164a000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e20101010101000001000909090900000000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"]
+ },
+ "Desc" : "Packet 1 with hold time 0 for 226.1.1.1/32"
+ },
+ "packet3" : {
+ "data": "01005e00000d005056961165080045c000944d0000000167453546000001e000000d2400e52b17c3000001004600000101000018e1010100080800000100090a090a0096650001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "BSR Prio - TC 4"
+ },
+ "packet4" : {
+ "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "225.1.1.1/32": ["9.9.9.9/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 5"
+ },
+ "packet5" : {
+ "data": "01005e00000d005056961165080045c000aa3d1c00000167550346000001e000000d24000d671c52000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a0096640001000020e1010101010100000100090909090000000001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 5, 225.1.1.1 with hold time 0"
+ },
+ "packet6" : {
+ "data": "01005e00000d005056961165080045c0008a795e0000016718e146000001e000000d24006cc509d5000001004600000101000018e10101000707000001000909090a0096660001000708090a00966700010007070907009668000100070702070096690001000705020700966a0001000702020700966b0001000202020200966c0001000020e20101010101000001000909090900960000",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"],
+ "226.1.1.1/32": ["9.9.9.9/32"]
+ },
+ "Desc" : "TC - 6,High prio rp removed on 225.1.1.0/24"
+ },
+ "packet7" : {
+ "data": "01005e00000d005056961165080045c0007e6ebb00000167239046000001e000000d2400090810b3000001004600000101000018e1010100080800000100020202020096640001000909090a0096640001000707020700966400010007020207009664000100070709070096640001000708090a00966400010007050207009664000100090a090a00966400",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["9.10.9.10/32", "7.8.9.10/32",
+ "9.9.9.10/32", "7.7.9.7/32",
+ "7.7.2.7/32", "7.5.2.7/32",
+ "7.2.2.7/32", "2.2.2.2/32"]
+ },
+ "Desc" : "TC - 8, rps with same priority"
+ },
+
+ "packet8" : {
+ "data": "01005e00000d005056b76687080045c000383cdf0000016755b246000001e000000d24008ad51a9f000001004600000101000020e1c86464010100000100d2d2d2d200960000",
+ "group": "225.200.100.100/32",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "70.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ },
+ "Desc" : "TC - 30, grp add with all octet"
+ },
+
+ "packet9" : {
+ "data": "01005e00000d005056b76687080045c000387b8600000167170b46000001e000000d2400c6282245000001000101020701000020e1c86464010100000100d2d2d2d200960000",
+ "group": "225.200.100.100/32",
+ "candidate_rp": "210.210.210.210/32",
+ "src_ip": "70.0.0.1/24",
+ "dest_ip": "70.0.0.2/24",
+ "bsr": "1.1.2.7/32",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ },
+ "Desc" : "TC -29, BSM with preferred ip"
+ }
+
+ }
+ }
+ },
+
+ "b2": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}
+ },
+ "bsm": {
+ "bsr_packets": {
+ "packet1" : {
+ "data": "01005e00000d005056b70489080045c0003865db0000016731b641000001e000000d2400659c0c6f000001004100000101000018e10101000101000001002121212100960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "65.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["33.33.33.33/32"],
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ }
+ },
+ "packet2" : {
+ "data": "01005e00000d005056b70489080045c00038663000000167316141000001e000000d24006dce0433000a01004100000101000018e10101000101000001002121212100960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "65.0.0.1/24",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.1.1.0/24": ["33.33.33.33/32"]
+ }
+ },
+
+ "packet3" : {
+ "data": "01005e00000d005056b76687080045c00038f5c800000167a1c841000001e000000d2400c6621a10000001000a02010101000020e1c86464010100000100d2d2d2d200960000",
+ "src_ip": "65.0.0.1/24",
+ "dest_ip": "65.0.0.2/24",
+ "bsr": "10.2.1.1/32",
+ "pkt_dst": "224.0.0.13",
+ "rp_mapping" : {
+ "225.200.100.100/32": ["210.210.210.210/32"]
+ }
+ }
+
+ }
+ }
+ },
+
+ "f1": {
+ "links": {
+ "b1": {"ipv4": "auto", "pim": "enable"},
+ "b2": {"ipv4": "auto", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "s1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "i1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "l1": {
+ "links": {
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "igmp": {
+ "interfaces": {
+ "l1-r1-eth1" :{
+ "igmp":{
+ "version": "2"
+ }
+ }
+ }
+ }
+ },
+ "s1": {
+ "links": {
+ "f1": {"ipv4": "auto", "pim": "enable"}
+ }
+ },
+ "r1": {
+ "links": {
+ "l1": {"ipv4": "auto", "pim": "disable"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
new file mode 100644
index 0000000..8a4ef1d
--- /dev/null
+++ b/tests/topotests/multicast_pim_bsm_topo2/test_mcast_pim_bsmp_02.py
@@ -0,0 +1,1119 @@
+#!/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 PIM BSM processing basic functionality:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Tests covered in this suite
+1. Verify (*,G) mroute detail on FRR router after BSM rp installed
+2. Verify group to RP updated correctly on FRR router, when BSR advertising
+ the overlapping group address
+3. Verify group to RP info is updated correctly, when BSR advertising the
+ same RP with different priority
+4. Verify group to RP mapping in FRR node when 2 BSR are present in the network
+ and both are having same BSR priority
+5. Verify RP is selected based on hash function, when BSR advertising the group
+ to RP mapping with same priority
+6. Verify fragmentation of bootstrap message
+7. Verify when candidate RP advertised with 32 mask length
+ and contain all the contacts
+"""
+
+import os
+import sys
+import time
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ addKernelRoute,
+ create_static_routes,
+ reset_config_on_routers,
+ run_frr_cmd,
+ required_linux_kernel_version,
+ topo_daemons,
+ verify_rib,
+)
+
+from lib.pim import (
+ add_rp_interfaces_and_pim_config,
+ reconfig_interfaces,
+ scapy_send_bsr_raw_packet,
+ find_rp_from_bsrp_info,
+ verify_pim_grp_rp_source,
+ verify_pim_bsr,
+ verify_mroutes,
+ verify_join_state_and_timer,
+ verify_pim_state,
+ verify_upstream_iif,
+ verify_igmp_groups,
+ verify_pim_upstream_rpf,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ McastTesterHelper,
+ verify_pim_neighbors,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
+TOPOLOGY = """
+
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+"""
+# Global variables
+NEXT_HOP1 = "70.0.0.1"
+NEXT_HOP2 = "65.0.0.1"
+BSR_IP_1 = "1.1.2.7"
+BSR_IP_2 = "10.2.1.1"
+BSR1_ADDR = "1.1.2.7/32"
+BSR2_ADDR = "10.2.1.1/32"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/mcast_pim_bsmp_02.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, " Verify PIM neighbor: Failed Error: {}".format(result)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def clear_bsrp_data(tgen, topo):
+
+ """
+ clear bsm databas after test"
+ Parameters
+ ----------
+ * `tgen`: topogen object
+
+ Usage
+ -----
+ result = clear_bsrp_data(tgen, topo)
+ Returns
+ -------
+ errormsg(str) or True
+ """
+
+ for dut in tgen.routers():
+
+ rnode = tgen.routers()[dut]
+
+ logger.info("[DUT: %s]: clear_bsrp_data")
+
+ run_frr_cmd(rnode, "clear ip pim bsr-data")
+
+ return True
+
+
+def pre_config_to_bsm(tgen, topo, tc_name, bsr, sender, receiver, fhr, rp, lhr, packet):
+ """
+ API to do required configuration to send and receive BSR packet
+ """
+
+ # Re-configure interfaces as per BSR packet
+ result = reconfig_interfaces(tgen, topo, bsr, fhr, packet)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Create static routes
+ if "bsr" in topo["routers"][bsr]["bsm"]["bsr_packets"][packet]:
+ bsr_route = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["bsr"]
+ next_hop = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["src_ip"].split(
+ "/"
+ )[0]
+ next_hop_rp = topo["routers"][fhr]["links"][rp]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0]
+
+ # Add static routes
+ input_dict = {
+ rp: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_rp}]},
+ lhr: {"static_routes": [{"network": bsr_route, "next_hop": next_hop_lhr}]},
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ for dut, _nexthop in zip([rp, lhr], [next_hop_rp, next_hop_lhr]):
+ input_routes = {dut: input_dict[dut]}
+ result = verify_rib(
+ tgen, "ipv4", dut, input_routes, _nexthop, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ # Add kernel route for source
+ group = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["pkt_dst"]
+ bsr_interface = topo["routers"][bsr]["links"][fhr]["interface"]
+ result = addKernelRoute(tgen, bsr, bsr_interface, group)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # RP Mapping
+ rp_mapping = topo["routers"][bsr]["bsm"]["bsr_packets"][packet]["rp_mapping"]
+
+ # Add interfaces in RP for all the RPs
+ result = add_rp_interfaces_and_pim_config(tgen, topo, "lo", rp, rp_mapping)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Add kernel routes to sender and receiver
+ for group, rp_list in rp_mapping.items():
+ mask = group.split("/")[1]
+ if int(mask) == 32:
+ group = group.split("/")[0]
+
+ # Add kernel routes for sender
+ s_interface = topo["routers"][sender]["links"][fhr]["interface"]
+ result = addKernelRoute(tgen, sender, s_interface, group)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Add kernel routes for receiver
+ r_interface = topo["routers"][receiver]["links"][lhr]["interface"]
+ result = addKernelRoute(tgen, receiver, r_interface, group)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Add static routes for RPs in FHR and LHR
+ next_hop_fhr = topo["routers"][rp]["links"][fhr]["ipv4"].split("/")[0]
+ next_hop_lhr = topo["routers"][rp]["links"][lhr]["ipv4"].split("/")[0]
+ input_dict = {
+ fhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_fhr}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(
+ tgen, "ipv4", fhr, input_dict, next_hop_fhr, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ lhr: {"static_routes": [{"network": rp_list, "next_hop": next_hop_lhr}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verifying static routes are installed
+ result = verify_rib(
+ tgen, "ipv4", lhr, input_dict, next_hop_lhr, protocol="static"
+ )
+ assert result is True, "Testcase {} : Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_starg_mroute_p0(request):
+ """
+ 1. Verify (*,G) mroute detail on FRR router after BSM rp installed
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "226.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check igmp groups
+ step("Verify IGMP groups in LHR l1")
+ dut = "l1"
+ intf = "l1-r1-eth1"
+ result = verify_igmp_groups(tgen, dut, intf, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ group = "226.1.1.1/32"
+ src_addr = "*"
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR in l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR in l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify join state and join timer
+ step("Verify join state and join timer in l1")
+ iif = "l1-i1-eth0"
+ result = verify_join_state_and_timer(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify upstream IIF interface
+ step("Verify upstream IIF interface in l1")
+ result = verify_upstream_iif(tgen, dut, iif, src_addr, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify IIF/OIL in pim state
+ oil = "l1-r1-eth1"
+ result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify ip mroute
+ step("Verify ip mroute in l1")
+ src_addr = "*"
+ result = verify_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Remove the group rp mapping and send bsm
+ step("Remove the grp-rp mapping by sending bsm with hold time 0 for grp-rp")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP unreachable
+ step("Check RP unreachability in l1")
+ iif = "Unknown"
+ result = verify_upstream_iif(
+ tgen, dut, iif, src_addr, GROUP_ADDRESS, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify that it is not installed
+ step("Verify that iif is not installed in l1")
+ iif = "<iif?>"
+ result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS, installed_fl=0)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify mroute not installed
+ step("Verify mroute not installed in l1")
+ result = verify_mroutes(
+ tgen, dut, src_addr, GROUP_ADDRESS, iif, oil, retry_timeout=20, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroute installed in l1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Send BSM again to configure rp
+ step("Add back RP by sending BSM from b1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify that (*,G) installed in mroute again
+ iif = "l1-i1-eth0"
+ result = verify_mroutes(tgen, dut, src_addr, GROUP_ADDRESS, iif, oil)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_overlapping_group_p0(request):
+ """
+ Verify group to RP updated correctly on FRR router, when BSR advertising
+ the overlapping group address
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet4")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 is chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ group1 = "225.1.1.1/32"
+ # Find the elected rp from bsrp-info fro group 225.1.1.1/32
+ step("Find the elected rp from bsrp-info in LHR for 225.1.1.1/32")
+ rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group1)
+ assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ group2 = "225.1.1.0/24"
+ # Find the elected rp from bsrp-info fro group 225.1.1.0/24
+ step("Find the elected rp from bsrp-info in LHR for 225.1.1.0/24")
+ rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group2)
+ assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ iif = "l1-i1-eth0"
+ # Verify upstream rpf for 225.1.1.1 is chosen as rp1
+ step("Verify upstream rpf for 225.1.1.1 is chosen as rp1 in l1")
+ result = verify_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp1)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b1 with rp for 225.1.1.1/32 removed
+ step("Send BSR packet from b1 with rp for 225.1.1.1/32 removed")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet5")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify upstream rpf for 225.1.1.1 is chosen as rp1
+ step("Verify upstream rpf for 225.1.1.1 is chosen as rp2 in l1")
+ result = verify_pim_upstream_rpf(tgen, topo, dut, iif, GROUP_ADDRESS, rp2)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify IIF/OIL in pim state
+ step("Verify iif is installed after rp change in l1")
+ oil = "l1-r1-eth1"
+ result = verify_pim_state(tgen, dut, iif, oil, GROUP_ADDRESS)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_RP_priority_p0(request):
+ """
+ Verify group to RP info is updated correctly, when BSR advertising the
+ same RP with different priority
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 is chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ group = "225.1.1.0/24"
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp1 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp1 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp1[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b1 after deleting high prio rp for 225.1.1.0/24
+ step("Send BSM from b1 to FHR deleting high prio rp for 225.1.1.0/24")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet6")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp2 is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+ logger.info("RP old: {} RP2 new: {} ".format(rp1[group], rp2[group]))
+
+ # Verify is the rp is different now
+ assert rp1[group] != rp2[group], "Testcase {} :Failed \n Error {}".format(
+ tc_name, result
+ )
+
+ rp_add1 = rp1[group]
+ rp_add2 = rp2[group]
+
+ # Verify if that rp is installed
+ step("Verify new RP in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add2)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b1 after putting back high prio rp for 225.1.1.0/24
+ step("Send BSM from b1 to FHR put back old high prio rp for 225.1.1.0/24")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR")
+ rp2 = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp2 is not {}, "Testcase {} :Failed \n Error : RP not Found".format(tc_name)
+
+ # Verify is the rp is different now
+ step("Verify now old RP is elected again")
+ assert (
+ rp_add1 == rp2[group]
+ ), "Testcase {} :Failed \n Error : rp expected {} rp received {}".format(
+ tc_name, rp_add1, rp2[group] if group in rp2 else None
+ )
+
+ # Verify if that rp is installed
+ step("Verify new RP in LHR installed")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp_add1)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_BSR_election_p0(request):
+ """
+ Verify group to RP mapping in FRR node when 2 BSR are present in the network
+ and both are having same BSR priority
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Send BSR packet from b1 to FHR")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet3")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip1 = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[
+ 0
+ ]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 is chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ dut = "l1"
+ group = "225.1.1.0/24"
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR in l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip1, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Send BSR packet from b2 with same priority
+ step("Send BSR packet from b2 to FHR with same priority")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b2", "f1", "packet1")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip2 = topo["routers"]["b2"]["bsm"]["bsr_packets"]["packet2"]["bsr"].split("/")[
+ 0
+ ]
+ time.sleep(1)
+
+ logger.info("BSR b1:" + bsr_ip1 + " BSR b2:" + bsr_ip2)
+ # Verify bsr state in FHR
+ step("Verify if b2 is not chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip2, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "b2 is chosen as bsr in f1 \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Verify if b1 is still chosen as bsr
+ step("Verify if b1 is still chosen as bsr in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip1)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify if that rp is installed
+ step("Verify that same RP in istalled in LHR l1")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_RP_hash_p0(request):
+ """
+ Verify RP is selected based on hash function, when BSR advertising the group
+ to RP mapping with same priority
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet7")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ dut = "l1"
+
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ group = "225.1.1.0/24"
+
+ # Find the elected rp from bsrp-info
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify if RP with highest hash value is chosen
+ step("Verify if RP(2.2.2.2) with highest hash value is chosen in l1")
+ if rp[group] == "2.2.2.2":
+ result = True
+ else:
+ result = "rp expected: 2.2.2.2 got:" + rp[group]
+
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Check RP detail in LHR
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_BSM_fragmentation_p1(request):
+ """
+ Verify fragmentation of bootstrap message
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b2", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ GROUP_ADDRESS = "225.1.1.1"
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet1"]["bsr"].split("/")[0]
+
+ step("Send BSM and verify if all routers have same bsrp before fragment")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet1")
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ fhr_node = tgen.routers()["f1"]
+ inter_node = tgen.routers()["i1"]
+ lhr_node = tgen.routers()["l1"]
+
+ # Verify if bsrp list is same across f1, i1 and l1
+ step("Verify if bsrp list is same across f1, i1 and l1")
+ bsrp_f1 = fhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json f1: \n %s", bsrp_f1)
+ bsrp_i1 = inter_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json i1: \n %s", bsrp_i1)
+ bsrp_l1 = lhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json l1: \n %s", bsrp_l1)
+
+ if bsrp_f1 == bsrp_l1:
+ result = True
+ else:
+ result = "bsrp info in f1 is not same in l1"
+
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments
+ step("set mtu of fhr(f1) to i1 interface to 100 so that bsm fragments")
+ fhr_node.run("ip link set f1-i1-eth2 mtu 100")
+ inter_node.run("ip link set i1-f1-eth0 mtu 100")
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet2")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ # Verify bsr state in FHR
+ step("Verify if b1 chosen as BSR")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ # Verify if bsrp list is same across f1, i1 and l1
+ step("Verify if bsrp list is same across f1, i1 and l1 after fragmentation")
+ bsrp_f1 = fhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json f1: \n %s", bsrp_f1)
+ bsrp_i1 = inter_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json i1: \n %s", bsrp_i1)
+ bsrp_l1 = lhr_node.vtysh_cmd("show ip pim bsrp-info json", isjson=True)
+ logger.info("show_ip_pim_bsrp_info_json l1: \n %s", bsrp_l1)
+
+ if bsrp_f1 == bsrp_l1:
+ result = True
+ else:
+ result = "bsrp info in f1 is not same in l1"
+
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+def test_RP_with_all_ip_octet_p1(request):
+ """
+ Verify when candidate RP advertised with 32 mask length
+ and contain all the contacts
+
+ Topology used:
+ b1_____
+ |
+ |
+ s1-----f1-----i1-----l1----r1
+ |
+ ______|
+ b2
+
+ b1 - BSR 1
+ b2 - BSR 2
+ s1 - Source
+ f1 - FHR
+ i1 - Intermediate Router (also RP)
+ r1 - Receiver
+
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("pre-configure BSM packet")
+ result = pre_config_to_bsm(
+ tgen, topo, tc_name, "b1", "s1", "r1", "f1", "i1", "l1", "packet1"
+ )
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Send the IGMP group (225.100.100.100) from receiver connected to FRR")
+ GROUP_ADDRESS = "225.200.100.100"
+
+ # Use scapy to send pre-defined packet from senser to receiver
+ step("Configure cisco-1 as BSR1")
+ result = scapy_send_bsr_raw_packet(tgen, topo, "b1", "f1", "packet8")
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ bsr_ip = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet8"]["bsr"].split("/")[0]
+ time.sleep(1)
+
+ result = app_helper.run_join("r1", GROUP_ADDRESS, "l1")
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ dut = "l1"
+ step(
+ "Groups are shown with candidate RP with correct mask length 'show ip pim bsrp-info'"
+ )
+ step("Verify if b1 chosen as BSR in f1")
+ result = verify_pim_bsr(tgen, topo, "f1", bsr_ip)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ group = topo["routers"]["b1"]["bsm"]["bsr_packets"]["packet9"]["group"]
+ step("Find the elected rp from bsrp-info in LHR l1")
+ rp = find_rp_from_bsrp_info(tgen, dut, bsr_ip, group)
+ assert rp is not {}, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP in LHR")
+ result = verify_pim_grp_rp_source(tgen, topo, dut, group, "BSR", rp[group])
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("clear BSM database before moving to next case")
+ clear_bsrp_data(tgen, topo)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/__init__.py b/tests/topotests/multicast_pim_dr_nondr_test/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/__init__.py
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json
new file mode 100644
index 0000000..4a4eb9a
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_ospf_topo2.json
@@ -0,0 +1,195 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "switches": {
+ "s1": {
+ "links": {
+ "i1": {"ipv4": "auto"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"}
+ }
+ }
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r5": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r5": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "i2": {
+ "ipv4": "auto",
+ "pim": "enable"
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "i1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r5": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json
new file mode 100644
index 0000000..9d7d766
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_static_routes_topo1.json
@@ -0,0 +1,85 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "switches": {
+ "s1": {
+ "links": {
+ "i1": {"ipv4": "auto"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "r3": {"ipv4": "auto", "pim": "enable"}
+ }
+ }
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "static_routes": [{
+ "network": ["10.0.0.1/24", "1.0.5.17/32"],
+ "next_hop": "10.0.1.2"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "static_routes": [{
+ "network": ["10.0.0.1/24", "1.0.5.17/32"],
+ "next_hop": "10.0.2.2"
+ }]
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"}
+ },
+ "static_routes": [{
+ "network": ["10.0.0.1/24", "1.0.5.17/32"],
+ "next_hop": "10.1.1.1"
+ }]
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "static_routes": [
+ {
+ "network": ["10.1.1.4/24"],
+ "next_hop": "10.0.2.1"
+ },
+ {
+ "network": ["10.0.0.1/24"],
+ "next_hop": "10.0.3.2"
+ }]
+ },
+ "r5": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"}
+ },
+ "static_routes": [{
+ "network": ["10.1.1.4/24", "1.0.5.17/32"],
+ "next_hop": "10.0.3.1"
+ }]
+ },
+ "i1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r5": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json
new file mode 100644
index 0000000..f078657
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/pim_dr_nondr_with_transit_router_topo3.json
@@ -0,0 +1,241 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "switches": {
+ "s1": {
+ "links": {
+ "i1": {"ipv4": "auto"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"}
+ }
+ }
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r6": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r6": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r6": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r6": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r6": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r5": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r6": {},
+ "r5": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "i2": {
+ "ipv4": "auto",
+ "pim": "enable"
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r6": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "pim": "enable",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "i1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r5": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
new file mode 100755
index 0000000..d0422e2
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_ospf_topo2.py
@@ -0,0 +1,1139 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. Configure IGMP local join on DR and non DR
+"""
+
+import os
+import sys
+import json
+import time
+import datetime
+from time import sleep
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ apply_raw_config,
+ add_interfaces_to_vlan,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ check_router_status,
+ topo_daemons,
+ required_linux_kernel_version,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_mroutes,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ verify_pim_config,
+ verify_upstream_iif,
+ verify_multicast_traffic,
+ verify_multicast_flag_state,
+ verify_igmp_groups,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+
+pytestmark = [pytest.mark.pimd]
+
+TOPOLOGY = """
+
+Descripton: Configuring OSPF on r1/r2/r4/r5 for RP reachablility.
+IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file
+JSON snippet:
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+
+ r5 ------- i2
+ 10.0.3.2/24| 10.0.0.2/24
+ |
+ 10.0.3.1/24|
+ ------------ r4 ----------
+ | 10.0.1.2/24 10.0.2.2/24 |
+ 10.0.1.1/24 | | 10.0.2.1/24
+ r1 ----------- s1 ---------- r2
+ 10.0.4.2/24 | 10.0.4.3/24
+ |
+ |10.0.4.1/24
+ i1
+
+ Description:
+ i1, i2 - FRR running iperf to send IGMP
+ join and traffic
+ r1, r2, r4, r5 - FRR router
+ s1 - OVS switch
+"""
+
+# Global variables
+VLAN_1 = 2501
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_JOIN = "225.1.1.1"
+VLAN_INTF_ADRESS_1 = "10.0.8.3/24"
+SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"}
+GROUP_RANGE_1 = ["225.1.1.1/32", "225.1.1.2/32"]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2"]
+GROUP_RANGE_2 = ["226.1.1.1/32", "226.1.1.2/32"]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2"]
+GROUP_RANGE_3 = ["227.1.1.1/32", "227.1.1.2/32"]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2"]
+
+intf_r1_s1 = None
+intf_r1_s1_addr = None
+intf_r2_s1 = None
+intf_r2_s1_addr = None
+intf_r3_s1 = None
+intf_r3_s1_addr = None
+intf_i1_s1 = None
+intf_i1_s1_addr = None
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ testdir = os.path.dirname(os.path.realpath(__file__))
+ json_file = "{}/pim_dr_nondr_with_ospf_topo2.json".format(testdir)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, tgen.json_topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, tgen.json_topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def pre_config_for_receiver_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for receiver test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r2_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r2_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable OSPF between r1 and r2")
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip ospf area 0.0.0.0",
+ "ip ospf dead-interval 4",
+ "ip ospf hello-interval 1",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic from R4 connected source")
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+def pre_config_for_source_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for source test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r2_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r2_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IGMP and PIM on switch connected receiver nodes , "
+ "configure PIM nbr with hello timer 1"
+ )
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)")
+
+ intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable OSPF between r1 and r2")
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip ospf area 0.0.0.0",
+ "ip ospf dead-interval 4",
+ "ip ospf hello-interval 1",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic from Source node")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_configuring_igmp_local_join_on_reciever_dr_non_dr_nodes_p1(request):
+ """
+ Configure IGMP local join on DR and non DR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure R1 is DR initially configuring highest IP on R1 and R2 "
+ "second highest, R1 is lower"
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ vlan_input = {
+ "r1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_r1_s1: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "r2": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_r2_s1: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r2_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1 + GROUP_RANGE_3,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP local join for groups 226.1.1.1 to 226.1.1.5")
+
+ vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1)
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_1}
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable OSPF between all the nodes")
+
+ step("Configure local join on R1 for group range (227.1.1.1)")
+
+ vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1)
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3}
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic from R4 connected source")
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["r5"]["interface"]}
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3, "r5")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R1, R2 and R2 has IGMP groups for 226.x.x.x and 227.1.1.1 groups")
+
+ intf_r1_s1 = "{}.{}".format(
+ topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1
+ )
+ intf_r2_s1 = "{}.{}".format(
+ topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1
+ )
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ result = verify_igmp_groups(
+ tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R1 is DR, R2 has 226.x.x.x and 227.1.1.1 (*,G) mroute with SC flag")
+ step("(S,G) mroute for 226.1.1.1 group present on R2")
+
+ source_i2 = topo["routers"]["i2"]["links"]["r5"]["ipv4"].split("/")[0]
+ input_dict_r2 = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["r4"]["interface"],
+ "oil": "{}.{}".format(
+ topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ }
+ ]
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, flag in zip(["r2"], ["SC"]):
+ step("{} has (*,G) flag as {}".format(dut, flag))
+ result = verify_multicast_flag_state(tgen, dut, "*", IGMP_JOIN_RANGE_1, flag)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete local join from DR node")
+ for _join in IGMP_JOIN_RANGE_3:
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {
+ "join": [_join],
+ "delete_attr": True,
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After removing local join 227.1.1.1 group removed from IGMP join "
+ "of R1, R2 node , using 'show ip igmp groups json'"
+ )
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ result = verify_igmp_groups(tgen, dut, intf, IGMP_JOIN_RANGE_3, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups are still present \n Error: {}".format(tc_name, result)
+ )
+
+ step("(*,G) mroute for 227.1.1.1 group removed from R1 node")
+ step(
+ "After remove of local join from R1 and R2 node verify (*,G) and (S,G) "
+ "mroutes should not present on R1, R2 and R3 nodes"
+ )
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure local join on R2 for group range (227.1.1.1)")
+
+ input_dict = {
+ "r2": {
+ "igmp": {
+ "interfaces": {
+ intf_r2_s1: {"igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3}}
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring local join on R2 non DR node, IGMP groups for 26.x.x.x and "
+ "227.1.1.1 present on all the nodes"
+ )
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ result = verify_igmp_groups(
+ tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R2 has 227.1.1.1 (*,G) mroute with SC flag")
+
+ for dut, flag in zip(["r2"], ["SC"]):
+ step("{} has (*,G) flag as {}".format(dut, flag))
+ result = verify_multicast_flag_state(tgen, dut, "*", IGMP_JOIN_RANGE_3, flag)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure local join on R1 for group range (227.1.1.1)")
+
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {"version": "2", "join": IGMP_JOIN_RANGE_3}
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After configuring 227.1.1.1 on R1 node, verify no change on IGMP groups on all the nodes"
+ )
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ result = verify_igmp_groups(
+ tgen, dut, intf, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R2 has 227.1.1.1 (*,G) mroute with SC flag")
+
+ step("r2 has (*,G) flag as SC")
+ result = verify_multicast_flag_state(tgen, "r2", "*", IGMP_JOIN_RANGE_3, "SC")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R1 should not have (*,G) join and (S,G) join present")
+
+ input_dict_r1 = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": "{}.{}".format(
+ topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["r4"]["interface"],
+ "oil": "{}.{}".format(
+ topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ },
+ ]
+
+ for data in input_dict_r1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove local join from DR and Non DR node")
+
+ input_dict = {
+ "r1": {
+ "igmp": {
+ "interfaces": {
+ vlan_intf_r1_s1: {
+ "igmp": {
+ "version": "2",
+ "join": IGMP_JOIN_RANGE_3,
+ "delete_attr": True,
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "igmp": {
+ "interfaces": {
+ intf_r2_s1: {
+ "igmp": {
+ "version": "2",
+ "join": IGMP_JOIN_RANGE_3,
+ "delete_attr": True,
+ }
+ }
+ }
+ }
+ },
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After remove of local join from R1 and R2 node verify (*,G) and (S,G) mroutes "
+ "should not present on R1, R2 nodes"
+ )
+
+ for data in input_dict_r1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \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/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py
new file mode 100755
index 0000000..4d17da5
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_static_routes_topo1.py
@@ -0,0 +1,940 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. Verify mroute while rebooting DR /Non DR nodes( r1, r2 , r3 on all the nodes)
+"""
+
+import os
+import sys
+import json
+import time
+import datetime
+from time import sleep
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ apply_raw_config,
+ add_interfaces_to_vlan,
+ stop_router,
+ start_router,
+ check_router_status,
+ topo_daemons,
+ required_linux_kernel_version,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_mroutes,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ verify_pim_config,
+ verify_upstream_iif,
+ verify_multicast_flag_state,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+
+pytestmark = [pytest.mark.pimd]
+
+TOPOLOGY = """
+
+Descripton: Configuring static routes on r1/r2/r3/r4/r5 for RP reachablility.
+IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file
+JSON snippet:
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+
+ r5 ------- i2
+ 10.0.3.2/24| 10.0.0.2/24
+ |
+ 10.0.3.1/24|
+ ------------ r4 ----------
+ | 10.0.1.2/24 10.0.2.2/24 |
+ 10.0.1.1/24 | | 10.0.2.1/24
+ r1 ----------- s1 ---------- r2
+ 10.0.4.2/24 | 10.0.4.3/24
+ |
+ |10.0.4.4/24
+ i1 -------- r3
+ 10.0.4.1/24
+ Description:
+ i1, i2 - FRR running iperf to send IGMP
+ join and traffic
+ r1, r2, r3, r4, r5 - FRR ruter
+ s1 - OVS switch
+"""
+
+# Global variables
+VLAN_1 = 2501
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_JOIN = "225.1.1.1"
+VLAN_INTF_ADRESS_1 = "10.0.8.3/24"
+SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"}
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+intf_r1_s1 = None
+intf_r1_s1_addr = None
+intf_r2_s1 = None
+intf_r2_s1_addr = None
+intf_r3_s1 = None
+intf_r3_s1_addr = None
+intf_i1_s1 = None
+intf_i1_s1_addr = None
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ testdir = os.path.dirname(os.path.realpath(__file__))
+ json_file = "{}/pim_dr_nondr_with_static_routes_topo1.json".format(testdir)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, tgen.json_topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, tgen.json_topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def pre_config_for_receiver_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for receiver test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_r3_s1, intf_r3_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_r3_s1 = topo["routers"]["r3"]["links"]["s1"]["interface"]
+ intf_r3_s1_addr = topo["routers"]["r3"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r3_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r3_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "r2": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_r2_s1: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_3["ip"],
+ "subnet": SAME_VLAN_IP_3["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r3": {
+ "raw_config": [
+ "interface {}".format(intf_r3_s1),
+ "no ip address {}".format(intf_r3_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r2_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_3["ip"], SAME_VLAN_IP_3["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2", "r3"], [intf_r1_s1, intf_r2_s1, intf_r3_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Using static routes instead OSPF: Enable OSPF between all the nodes")
+
+ step("Start traffic from R4 connected source")
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+def pre_config_for_source_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for source test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_r3_s1, intf_r3_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_r3_s1 = topo["routers"]["r3"]["links"]["s1"]["interface"]
+ intf_r3_s1_addr = topo["routers"]["r3"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r3_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r3_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "r2": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_r2_s1: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_3["ip"],
+ "subnet": SAME_VLAN_IP_3["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r3": {
+ "raw_config": [
+ "interface {}".format(intf_r3_s1),
+ "no ip address {}".format(intf_r3_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IGMP and PIM on switch connected receiver nodes , "
+ "configure PIM nbr with hello timer 1"
+ )
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r2_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_3["ip"], SAME_VLAN_IP_3["cidr"]),
+ "ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2", "r3"], [intf_r1_s1, intf_r2_s1, intf_r3_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)")
+
+ intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["r5"]["interface"]}
+
+ result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Using static routes instead OSPF: Enable OSPF between all the nodes")
+
+ step("Start traffic from Source node")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_pim_source_dr_functionality_while_rebooting_dr_non_dr_nodes_p1(request):
+ """
+ Verify mroute while rebooting DR /Non DR nodes( r1, r2 , r3 on all the nodes)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ result = pre_config_for_source_dr_tests(tgen, topo, tc_name, "r1", "r3")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R1 is the DR , verify using 'show ip pim interface json'")
+
+ vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1)
+ input_dict_dr = {
+ "r1": {
+ "pim": {
+ "interfaces": {vlan_intf_r1_s1: {"drAddress": SAME_VLAN_IP_3["ip"]}}
+ }
+ }
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "R2 is transit router for I1 to reach R4, mroute should have (s, g) mroute with "
+ "OIL towards R4, using 'show ip mroute json'"
+ )
+ step(
+ "R2 (s, g) upstream should be in join state verify using "
+ "'show ip pim upstream json'"
+ )
+ step(
+ "R1 has (S, G) mroute with NONE OIL and upstream as not joined, verify using "
+ "'show ip mroute json' 'show ip pim upstream json'"
+ )
+
+ source_i1 = SAME_VLAN_IP_4["ip"]
+ input_dict_r1_r2 = [
+ {
+ "dut": "r1",
+ "src_address": source_i1,
+ "oil": "none",
+ "iif": "{}.{}".format(
+ topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i1,
+ "oil": topo["routers"]["r2"]["links"]["r4"]["interface"],
+ "iif": "{}.{}".format(
+ topo["routers"]["r2"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ },
+ ]
+
+ for data in input_dict_r1_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ if data["dut"] == "r2":
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Upstream is still joined state \n Error: {}".format(tc_name, result)
+ )
+
+ step("Reboot R3 node")
+ stop_router(tgen, "r3")
+
+ step(
+ "After reboot of R3 verify R1 continues to be DR, using 'show ip pim interface json'"
+ )
+
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R3 should not have any mroute and upstream")
+ step("R2 has mroute with OIL towards R4 /R1 , verify using 'show ip mroute'")
+ step(
+ "R2 has upstream with Join RejP state verify using 'show ip pim upstream json'"
+ )
+ step("R1 has mroute with none OIL and upstream with Not Join")
+
+ for data in input_dict_r1_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ if data["dut"] == "r2":
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+ else:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Upstream is still joined state \n Error: {}".format(tc_name, result)
+ )
+
+ step("Reboot R2 node")
+ stop_router(tgen, "r2")
+
+ step(
+ "After reboot of R2, R1 continues to be DR verify using 'show ip pim interface json'"
+ )
+
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "R3 and R2 should not have any mroute and upstream , verify using "
+ "'show ip mroute json' 'show ip pim upstream json'"
+ )
+ step("R1 has mroute created with empty OIL, using 'show ip mroute json'")
+ step(
+ "R1 has upstream with Not Join, Rej Prune , verify using 'show ip pim upstream json'"
+ )
+
+ for data in input_dict_r1_r2:
+ if data["dut"] == "r1":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "Upstream is still joined state \n Error: {}".format(tc_name, result)
+ )
+
+ step("Reboot R1 node using FRR stop")
+ stop_router(tgen, "r1")
+
+ step(
+ "After stop of all the routers, verify upstream and mroutes should "
+ "not present in any of them"
+ )
+
+ for data in input_dict_r1_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("start FRR for all the nodes")
+ start_router(tgen, "r1")
+ start_router(tgen, "r2")
+ start_router(tgen, "r3")
+
+ step("After start of all the routers, R1 became DR")
+
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_r1_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ if data["dut"] == "r2":
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed 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/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py
new file mode 100755
index 0000000..a5d2730
--- /dev/null
+++ b/tests/topotests/multicast_pim_dr_nondr_test/test_pim_dr_nondr_with_transit_router_topo3.py
@@ -0,0 +1,829 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. Verify mroutes when transit router present between RP and Source DR
+"""
+
+import os
+import sys
+import json
+import time
+import datetime
+from time import sleep
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ apply_raw_config,
+ add_interfaces_to_vlan,
+ check_router_status,
+ topo_daemons,
+ required_linux_kernel_version,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_mroutes,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ verify_pim_config,
+ verify_upstream_iif,
+ verify_multicast_traffic,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+
+pytestmark = [pytest.mark.pimd]
+
+TOPOLOGY = """
+
+Descripton: Configuring OSPF on r1/r2/r4/r5/r6 for RP reachablility, We have r6 as a transit router between
+ r1/r2 and r4.
+IPs are assigned automatically to routers, start IP and subnet is defined in respective JSON file
+JSON snippet:
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+
+ r5 ------- i2
+ 10.0.3.2/24| 10.0.0.2/24
+ |
+ 10.0.3.1/24|
+ r4
+ |10.0.4.1/24
+ |
+ 10.0.4.2/24|
+ ------------ r6 ----------
+ | 10.0.1.2/24 10.0.2.2/24 |
+ 10.0.1.1/24 | | 10.0.2.1/24
+ r1 ----------- s1 ---------- r2
+ 10.0.5.2/24 | 10.0.5.3/24
+ |
+ |10.0.5.1/24
+ i1
+
+
+
+ Description:
+ i1, i2 - FRR running iperf to send IGMP
+ join and traffic
+ r1, r2, r4, r5, r6 - FRR ruter
+ s1 - OVS switch
+"""
+
+# Global variables
+VLAN_1 = 2501
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_JOIN = "225.1.1.1"
+VLAN_INTF_ADRESS_1 = "10.0.8.3/24"
+SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"}
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+intf_r1_s1 = None
+intf_r1_s1_addr = None
+intf_r2_s1 = None
+intf_r2_s1_addr = None
+intf_r3_s1 = None
+intf_r3_s1_addr = None
+intf_i1_s1 = None
+intf_i1_s1_addr = None
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ testdir = os.path.dirname(os.path.realpath(__file__))
+ json_file = "{}/pim_dr_nondr_with_transit_router_topo3.json".format(testdir)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, tgen.json_topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, tgen.json_topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def pre_config_for_receiver_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for receiver test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r2_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r2_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ "ip igmp",
+ "ip igmp version 2",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP join for groups 226.1.1.1 to 226.1.1.5")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, join_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable OSPF between r1 and r2")
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip ospf area 0.0.0.0",
+ "ip ospf dead-interval 4",
+ "ip ospf hello-interval 1",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic from R4 connected source")
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+def pre_config_for_source_dr_tests(
+ tgen, topo, tc_name, highest_priority, lowest_priority
+):
+ """
+ API to do common pre-configuration for source test cases
+
+ parameters:
+ -----------
+ * `tgen`: topogen object
+ * `topo`: input json data
+ * `tc_name`: caller test case name
+ * `highest_priority`: router which will be having highest DR priority
+ * `lowest_priority`: router which will be having lowest DR priority
+ """
+
+ global intf_r1_s1, intf_r1_s1_addr, intf_r2_s1, intf_r2_s1_addr, intf_i1_s1, intf_i1_s1_addr
+
+ step("Configure IGMP and PIM on switch connected receiver nodes")
+ step("Configure PIM on all upstream interfaces")
+
+ step("Configure link between R1, R2 ,R3 and receiver on" " same vlan")
+ step(
+ "Make sure {0} is DR initially configuring highest IP on {0} and R2 "
+ "second highest, {1} is lower".format(highest_priority, lowest_priority)
+ )
+
+ intf_r1_s1 = topo["routers"]["r1"]["links"]["s1"]["interface"]
+ intf_r1_s1_addr = topo["routers"]["r1"]["links"]["s1"]["ipv4"]
+
+ intf_r2_s1 = topo["routers"]["r2"]["links"]["s1"]["interface"]
+ intf_r2_s1_addr = topo["routers"]["r2"]["links"]["s1"]["ipv4"]
+
+ intf_i1_s1 = topo["routers"]["i1"]["links"]["s1"]["interface"]
+ intf_i1_s1_addr = topo["routers"]["i1"]["links"]["s1"]["ipv4"]
+
+ if lowest_priority == "r1":
+ lowest_pr_intf = intf_r1_s1
+ else:
+ lowest_pr_intf = intf_r2_s1
+
+ if highest_priority == "r1":
+ highest_pr_intf = intf_r1_s1
+ else:
+ highest_pr_intf = intf_r2_s1
+
+ vlan_input = {
+ lowest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ lowest_pr_intf: {
+ "ip": SAME_VLAN_IP_1["ip"],
+ "subnet": SAME_VLAN_IP_1["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ highest_priority: {
+ "vlan": {
+ VLAN_1: [
+ {
+ highest_pr_intf: {
+ "ip": SAME_VLAN_IP_2["ip"],
+ "subnet": SAME_VLAN_IP_2["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ "i1": {
+ "vlan": {
+ VLAN_1: [
+ {
+ intf_i1_s1: {
+ "ip": SAME_VLAN_IP_4["ip"],
+ "subnet": SAME_VLAN_IP_4["subnet"],
+ }
+ }
+ ]
+ }
+ },
+ }
+
+ add_interfaces_to_vlan(tgen, vlan_input)
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}".format(intf_r1_s1),
+ "no ip address {}".format(intf_r1_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}".format(intf_r2_s1),
+ "no ip address {}".format(intf_r2_s1_addr),
+ "no ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}".format(intf_i1_s1),
+ "no ip address {}".format(intf_i1_s1_addr),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IGMP and PIM on switch connected receiver nodes , "
+ "configure PIM nbr with hello timer 1"
+ )
+
+ raw_config = {
+ lowest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(lowest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ "ip pim",
+ ]
+ },
+ highest_priority: {
+ "raw_config": [
+ "interface {}.{}".format(highest_pr_intf, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ "ip pim",
+ ]
+ },
+ "i1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_i1_s1, VLAN_1),
+ "ip address {}/{}".format(SAME_VLAN_IP_4["ip"], SAME_VLAN_IP_4["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip pim hello {} {}".format(HELLO_TIMER, HOLD_TIMER),
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure R4 as RP on all the nodes for group range 224.0.0.0/24")
+
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r4"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure IGMP on R5 port and send IGMP join for groups " "(226.1.1.1-5)")
+
+ intf_r5_i2 = topo["routers"]["r5"]["links"]["i2"]["interface"]
+ input_dict = {
+ "r5": {"igmp": {"interfaces": {intf_r5_i2: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i2", IGMP_JOIN_RANGE_1, "r5")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable OSPF between r1 and r2")
+
+ for dut, intf in zip(["r1", "r2"], [intf_r1_s1, intf_r2_s1]):
+ raw_config = {
+ dut: {
+ "raw_config": [
+ "interface {}.{}".format(intf, VLAN_1),
+ "ip ospf area 0.0.0.0",
+ "ip ospf dead-interval 4",
+ "ip ospf hello-interval 1",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic from Source node")
+
+ vlan_intf_i1_s1 = "{}.{}".format(intf_i1_s1, VLAN_1)
+ result = app_helper.run_traffic("i1", IGMP_JOIN_RANGE_1, bind_intf=vlan_intf_i1_s1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_mroute_when_transit_router_present_between_rp_and_source_dr_p1(request):
+ """
+ Verify mroutes when transit router present between RP and Source DR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ result = pre_config_for_source_dr_tests(tgen, topo, tc_name, "r1", "r2")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Taken care in base config: Add router R6 between RP and R1 , R2")
+
+ step("R1 is the DR, mroute and upstream created on R1")
+
+ vlan_intf_r1_s1 = "{}.{}".format(intf_r1_s1, VLAN_1)
+ input_dict_dr = {
+ "r1": {
+ "pim": {
+ "interfaces": {vlan_intf_r1_s1: {"drAddress": SAME_VLAN_IP_2["ip"]}}
+ }
+ }
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("On R1 Mroute OIL towards R6, upstream in join Rej Prune state")
+
+ source_i1 = SAME_VLAN_IP_4["ip"]
+ input_dict_r1 = [
+ {
+ "dut": "r1",
+ "src_address": source_i1,
+ "oil": topo["routers"]["r1"]["links"]["r6"]["interface"],
+ "iif": "{}.{}".format(
+ topo["routers"]["r1"]["links"]["s1"]["interface"], VLAN_1
+ ),
+ }
+ ]
+
+ for data in input_dict_r1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "R5 have mroute created and traffic is received, verify using "
+ "'show ip mroute json' 'show ip multicast json'"
+ )
+
+ input_dict_r5 = [
+ {
+ "dut": "r5",
+ "src_address": "*",
+ "iif": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r5"]["links"]["i2"]["interface"],
+ },
+ {
+ "dut": "r5",
+ "src_address": source_i1,
+ "iif": topo["routers"]["r5"]["links"]["r4"]["interface"],
+ "oil": topo["routers"]["r5"]["links"]["i2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_r5:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict_traffic_r5 = {
+ "r5": {
+ "traffic_received": [topo["routers"]["r5"]["links"]["r4"]["interface"]],
+ "traffic_sent": [topo["routers"]["r5"]["links"]["i2"]["interface"]],
+ }
+ }
+
+ result = verify_multicast_traffic(tgen, input_dict_traffic_r5)
+ assert result is True, "Testcase {}:Failed \n Error: {}".format(tc_name, result)
+
+ step("Make R2 as DR configuring higher priority value")
+
+ raw_config = {
+ "r1": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r1_s1, VLAN_1),
+ "no ip address {}/{}".format(
+ SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_1["cidr"]
+ ),
+ "ip address {}/{}".format(SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]),
+ ]
+ },
+ "r2": {
+ "raw_config": [
+ "interface {}.{}".format(intf_r2_s1, VLAN_1),
+ "no ip address {}/{}".format(
+ SAME_VLAN_IP_1["ip"], SAME_VLAN_IP_1["cidr"]
+ ),
+ "ip address {}/{}".format(SAME_VLAN_IP_2["ip"], SAME_VLAN_IP_2["cidr"]),
+ ]
+ },
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("R2 is the DR, mroute and upstream created on R2")
+
+ vlan_intf_r2_s1 = "{}.{}".format(intf_r2_s1, VLAN_1)
+ input_dict_dr = {
+ "r2": {
+ "pim": {
+ "interfaces": {vlan_intf_r2_s1: {"drAddress": SAME_VLAN_IP_2["ip"]}}
+ }
+ }
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "R5 have mroute created and traffic is received, verify using "
+ "'show ip mroute json' 'show ip multicast json'"
+ )
+
+ for data in input_dict_r5:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_dict_traffic_r5)
+ 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/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json b/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json
new file mode 100644
index 0000000..cc20abb
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo1/multicast_pim_sm_topo1.json
@@ -0,0 +1,146 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "l1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i6": {"ipv4": "auto", "pim": "enable"},
+ "i7": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "query": {"query-max-response-time": 40, "query-interval": 5},
+ "version": "2"
+ }
+ }
+ }
+ },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.7.0/24", "10.0.6.0/24", "10.0.9.0/24"],
+ "next_hop": "10.0.12.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24", "10.0.3.0/24"],
+ "next_hop": "10.0.2.1"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"],
+ "next_hop": "10.0.7.1"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.0.0/24", "10.0.11.0/24", "10.0.1.0/24", "10.0.2.0/24"],
+ "next_hop": "10.0.12.1"
+ }]
+ },
+ "f1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.7.2"
+ },
+ {
+ "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.0.0/24", "10.0.4.0/24", "1.0.1.2/32"],
+ "next_hop": "10.0.3.1"
+ }]
+ },
+ "c1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "i4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.2.2"
+ },
+ {
+ "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"],
+ "next_hop": "10.0.0.2"
+ }]
+ },
+ "c2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.3.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.4.0/24", "10.0.2.0/24"],
+ "next_hop": "10.0.0.1"
+ }]
+ },
+ "i1": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "c1": {"ipv4": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "c2": {"ipv4": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i8": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
new file mode 100755
index 0000000..b46885c
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo1/test_multicast_pim_sm_topo1.py
@@ -0,0 +1,1608 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. TC_1_1: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+2. TC_1_2: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+3. TC_4: Verify removing the RP should not impact the multicast
+ data traffic
+4. TC_5: Verify (*,G) and (S,G) entry populated again after clear the
+ PIM nbr and mroute from FRR node
+5. TC_9: Verify (s,g) timeout from FHR and RP when same receive
+ exist in LHR , FHR and RP
+6. TC_19: Verify mroute detail when same receiver joining 5
+ different sources
+7. TC_16: Verify (*,G) and (S,G) populated correctly
+ when FRR is the transit router
+8. TC_23: Verify (S,G) should not create if RP is not reachable
+9. TC_24: Verify modification of IGMP query timer should get update
+ accordingly
+10. TC_25: Verify modification of IGMP max query response timer
+ should get update accordingly
+"""
+
+import os
+import sys
+import time
+from time import sleep
+import pytest
+
+pytestmark = pytest.mark.pimd
+
+# 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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ apply_raw_config,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_mroutes,
+ get_pim_interface_traffic,
+ verify_upstream_iif,
+ verify_pim_join,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ verify_igmp_config,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.pimd]
+
+TOPOLOGY = """
+
+
+ i4-----c1-------------c2---i5
+ | |
+ | |
+ i1-----l1------r2-----f1---i2
+ | | | |
+ | | | |
+ i7 i6 i3 i8
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP
+ join and traffic
+ l1 - LHR (last hop router)
+ f1 - FHR (first hop router)
+ r2 - FRR router
+ c1 - FRR router
+ c2 - FRR router
+"""
+
+# Global variables
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_JOIN = "225.1.1.1"
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ testdir = os.path.dirname(os.path.realpath(__file__))
+ json_file = "{}/multicast_pim_sm_topo1.json".format(testdir)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, tgen.json_topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, tgen.json_topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # 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 verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_multicast_data_traffic_static_RP_send_join_then_traffic_p0(request):
+ """
+ TC_1_1: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+
+ step("get joinRx value before join")
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ state_dict = {"r2": {intf_r2_l1: ["joinRx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, state_before
+ )
+
+ result = app_helper.run_join("i1", IGMP_JOIN, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP join first and then start the traffic")
+
+ step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": intf_l1_r2, "oil": intf_l1_i1},
+ {"dut": "l1", "src_address": source, "iif": intf_l1_r2, "oil": intf_l1_i1},
+ {"dut": "r2", "src_address": "*", "iif": "lo", "oil": intf_r2_l1},
+ {"dut": "r2", "src_address": source, "iif": intf_r2_f1, "oil": intf_r2_l1},
+ {"dut": "f1", "src_address": source, "iif": intf_f1_i2, "oil": intf_f1_r2},
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes"
+ )
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value after join sent")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "l1 sent PIM (*,G) join to r2 verify using"
+ "'show ip pim interface traffic' on RP connected interface"
+ )
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("l1 sent PIM (S,G) join to f1 , verify using 'show ip pim join'")
+ dut = "f1"
+ interface = intf_f1_r2
+ result = verify_pim_join(tgen, topo, dut, interface, IGMP_JOIN)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_multicast_data_traffic_static_RP_send_traffic_then_join_p0(request):
+ """
+ TC_1_2: Verify Multicast data traffic with static RP, (*,g) and
+ (s,g) OIL updated correctly
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start traffic first and then send the IGMP join")
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+ step("joinRx value before join sent")
+ state_dict = {"r2": {"r2-l1-eth2": ["joinRx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = app_helper.run_join("i1", IGMP_JOIN, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "r2", "src_address": "*", "iif": "lo", "oil": "r2-l1-eth2"},
+ {"dut": "r2", "src_address": source, "iif": "r2-f1-eth0", "oil": "r2-l1-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ ]
+ # On timeout change from default of 80 to 120: failures logs indicate times 90+
+ # seconds for success on the 2nd entry in the above table. Using 100s here restores
+ # previous 80 retries with 2s wait if we assume .5s per vtysh/show ip mroute runtime
+ # (41 * (2 + .5)) == 102.
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN,
+ data["iif"],
+ data["oil"],
+ retry_timeout=102,
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes"
+ )
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("joinRx value after join sent")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "l1 sent PIM (*,G) join to r2 verify using"
+ "'show ip pim interface traffic' on RP connected interface"
+ )
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("l1 sent PIM (S,G) join to f1 , verify using 'show ip pim join'")
+ dut = "f1"
+ interface = "f1-r2-eth3"
+ result = verify_pim_join(tgen, topo, dut, interface, IGMP_JOIN)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_clear_pim_neighbors_and_mroute_p0(request):
+ """
+ TC_5: Verify (*,G) and (S,G) entry populated again after clear the
+ PIM nbr and mroute from FRR node
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP on c1 for group (225.1.1.1-5)")
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join 225.1.1.1 "
+ "to 225.1.1.5 from different interfaces"
+ )
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3, wait for SPT switchover")
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify clear ip mroute (*,g) entries are populated by using "
+ "'show ip mroute' cli"
+ )
+
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"}
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear mroutes on l1")
+ clear_mroute(tgen, "l1")
+
+ step(
+ "After clear ip mroute (*,g) entries are re-populated again"
+ " with same OIL and IIF, verify using 'show ip mroute' and "
+ " 'show ip pim upstream' "
+ )
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes"
+ )
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN
+ )
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_same_receiver_in_FHR_LHR_and_RP_p0(request):
+ """
+ TC_9: Verify (s,g) timeout from FHR and RP when same receive
+ exist in LHR , FHR and RP
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1) to R1")
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ },
+ "r2": {
+ "igmp": {
+ "interfaces": {
+ "r2-i3-eth1": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ },
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0", "i3": "i3-r2-eth0"}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from R3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("IGMP is received on FRR1 , FRR2 , FRR3, using " "'show ip igmp groups'")
+ igmp_groups = {"l1": "l1-i1-eth1", "r2": "r2-i3-eth1", "f1": "f1-i8-eth2"}
+ for dut, interface in igmp_groups.items():
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN, retry_timeout=80)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) present on all the node with correct OIL" " using 'show ip mroute'")
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "r2", "src_address": "*", "iif": "lo", "oil": "r2-i3-eth1"},
+ {"dut": "r2", "src_address": source, "iif": "r2-f1-eth0", "oil": "r2-i3-eth1"},
+ {"dut": "f1", "src_address": "*", "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_same_receiver_joining_5_diff_sources_p0(request):
+ """
+ TC_19: Verify mroute detail when same receiver joining 5
+ different sources
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) and (232.1.1.1-5)" " in c1")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IGMP interface on FRR1 and FRR3 and send IGMP join"
+ "for group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ result = app_helper.run_join("i1", _IGMP_JOIN_RANGE, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i8", _IGMP_JOIN_RANGE, "f1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send multicast traffic from all the sources to all the "
+ "receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_traffic = {
+ "i6": "i6-l1-eth0",
+ "i7": "i7-l1-eth0",
+ "i3": "i3-r2-eth0",
+ "i4": "i4-c1-eth0",
+ "i5": "i5-c2-eth0",
+ }
+
+ for src, src_intf in input_traffic.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify (*,G) are created on FRR1 and FRR3 node " " 'show ip mroute' ")
+
+ source_i7 = topo["routers"]["i7"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0]
+ source_i3 = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {
+ "dut": "l1",
+ "src_address": source_i5,
+ "iif": "l1-c1-eth0",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": "l1-r2-eth4",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": "l1-i6-eth2",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i7,
+ "iif": "l1-i7-eth3",
+ "oil": "l1-i1-eth1",
+ },
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {
+ "dut": "f1",
+ "src_address": source_i5,
+ "iif": "f1-c2-eth0",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i3,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i6,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i7,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the source one by one on FRR1")
+ input_intf = {"i6": "i6-l1-eth0", "i7": "i7-l1-eth0"}
+ for dut, intf in input_intf.items():
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "After removing the source verify traffic is stopped"
+ " immediately and (S,G) got timeout in sometime"
+ )
+
+ logger.info("After shut, waiting for SG timeout")
+
+ input_dict = [
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": "l1-i6-eth2",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i7,
+ "iif": "l1-i7-eth3",
+ "oil": "l1-i1-eth1",
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "Source which is stopped got removed , other source"
+ " after still present verify using 'show ip mroute' "
+ )
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {
+ "dut": "l1",
+ "src_address": source_i5,
+ "iif": "l1-c1-eth0",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": "l1-r2-eth4",
+ "oil": "l1-i1-eth1",
+ },
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {
+ "dut": "f1",
+ "src_address": source_i5,
+ "iif": "f1-c2-eth0",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i3,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Start all the source again for all the receivers")
+ input_intf = {"i6": "i6-l1-eth0", "i7": "i7-l1-eth0"}
+ for dut, intf in input_intf.items():
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After starting source all the mroute entries got populated, "
+ "no duplicate entries present in mroute verify 'show ip mroute'"
+ )
+
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {
+ "dut": "l1",
+ "src_address": source_i5,
+ "iif": "l1-c1-eth0",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": "l1-r2-eth4",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": "l1-i6-eth2",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i7,
+ "iif": "l1-i7-eth3",
+ "oil": "l1-i1-eth1",
+ },
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {
+ "dut": "f1",
+ "src_address": source_i5,
+ "iif": "f1-c2-eth0",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i3,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i6,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i7,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_frr_is_transit_router_p2(request):
+ """
+ TC_16: Verify (*,G) and (S,G) populated correctly
+ when FRR is the transit router
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c2")
+ input_dict = {
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-5) to FRR1")
+ result = app_helper.run_join("i1", IGMP_JOIN_RANGE_1, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1-5 receivers")
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Stop r2 router to make r2 router disabled from topology
+ input_intf = {"l1": "l1-r2-eth4", "f1": "f1-r2-eth3"}
+ for dut, intf in input_intf.items():
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "FRR4 has (S,G) and (*,G) ,created where incoming interface"
+ " toward FRR3 and OIL toward R2, verify using 'show ip mroute'"
+ " 'show ip pim state' "
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "c2", "src_address": "*", "iif": "lo", "oil": "c2-c1-eth0"},
+ {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "c2-c1-eth0"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop multicast traffic from FRR3")
+ dut = "i2"
+ intf = "i2-f1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ logger.info("Waiting for 20 sec to get traffic to be stopped..")
+ sleep(20)
+
+ step("top IGMP receiver from FRR1")
+ dut = "i1"
+ intf = "i1-l1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ logger.info("Waiting for 20 sec to get mroutes to be flused out..")
+ sleep(20)
+
+ step(
+ "After stopping receiver (*,G) also got timeout from transit"
+ " router 'show ip mroute'"
+ )
+
+ result = verify_mroutes(
+ tgen, "c1", "*", IGMP_JOIN, "c1-c2-eth1", "c1-l1-eth0", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_RP_unreachable_p1(request):
+ """
+ TC_23: Verify (S,G) should not create if RP is not reachable
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure RP on FRR2 (loopback interface) for " "the group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+
+ result = app_helper.run_join("i1", IGMP_JOIN, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure one IGMP interface on FRR3 node and send IGMP" " join (225.1.1.1)")
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i8", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ # Verify mroutes are present in FRR3(f1)
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the RP connected interface from f1 ( r2 to f1) link")
+ dut = "f1"
+ intf = "f1-r2-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ logger.info("Waiting for 20 sec to get mroutes to be flushed out..")
+ sleep(20)
+
+ step("Clear the mroute on f1")
+ clear_mroute(tgen, "f1")
+
+ step(
+ "After Shut the RP interface and clear the mroute verify all "
+ "(*,G) and (S,G) got timeout from FRR3 node , verify using "
+ " 'show ip mroute' "
+ )
+
+ result = verify_mroutes(
+ tgen, "f1", "*", IGMP_JOIN, "f1-r2-eth3", "f1-i8-eth2", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step("IGMP groups are present verify using 'show ip igmp group'")
+ dut = "l1"
+ interface = "l1-i1-eth1"
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_igmp_query_timer_p0(request):
+ """
+ TC_24:
+ Verify modification of IGMP query timer should get update
+ accordingly
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+ result = app_helper.run_join("i1", IGMP_JOIN, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_4 = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ ]
+ for data in input_dict_4:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes"
+ )
+ for data in input_dict_4:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Modify IGMP query interval default to other timer on FRR1" "3 times")
+ input_dict_1 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"query-interval": 20}}}
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"query-interval": 25}}}
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"query-interval": 30}}}
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_modify_igmp_max_query_response_timer_p0(request):
+ """
+ TC_25:
+ Verify modification of IGMP max query response timer
+ should get update accordingly
+ """
+
+ tgen = get_topogen()
+ topo = tgen.json_topo
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Enable IGMP on FRR1 interface and send IGMP join (225.1.1.1)")
+ result = app_helper.run_join("i1", IGMP_JOIN, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure IGMP query response time to 10 deci-sec on FRR1")
+ input_dict_1 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 10},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_1)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP on R2 (loopback interface) for the" " group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1 receiver")
+ result = app_helper.run_traffic("i2", IGMP_JOIN, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip mroute' showing correct RPF and OIF"
+ " interface for (*,G) and (S,G) entries on all the nodes"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_5 = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ ]
+ for data in input_dict_5:
+ result = verify_mroutes(
+ tgen, data["dut"], data["src_address"], IGMP_JOIN, data["iif"], data["oil"]
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify 'show ip pim upstream' showing correct OIL and IIF" " on all the nodes"
+ )
+ for data in input_dict_5:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete the PIM and IGMP on FRR1")
+ raw_config = {"l1": {"raw_config": ["interface l1-i1-eth1", "no ip pim"]}}
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ "query": {"query-max-response-time": 10, "delete": True},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure PIM on FRR")
+ result = create_pim_config(tgen, topo["routers"])
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure max query response timer 100 decisec on FRR1")
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 100},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove and add max query response timer cli with different"
+ "timer 5 times on FRR1 Enable IGMP and IGMP version 2 on FRR1"
+ " on FRR1"
+ )
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 105},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 110},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 115},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "query": {"query-max-response-time": 120},
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable IGMP and IGMP version 2 on FRR1 on FRR1")
+
+ input_dict_4 = {
+ "l1": {"igmp": {"interfaces": {"l1-i1-eth1": {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed 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/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json b/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json
new file mode 100644
index 0000000..cc20abb
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo2/multicast_pim_sm_topo2.json
@@ -0,0 +1,146 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "l1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i6": {"ipv4": "auto", "pim": "enable"},
+ "i7": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "query": {"query-max-response-time": 40, "query-interval": 5},
+ "version": "2"
+ }
+ }
+ }
+ },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.7.0/24", "10.0.6.0/24", "10.0.9.0/24"],
+ "next_hop": "10.0.12.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24", "10.0.3.0/24"],
+ "next_hop": "10.0.2.1"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24"],
+ "next_hop": "10.0.7.1"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.0.0/24", "10.0.11.0/24", "10.0.1.0/24", "10.0.2.0/24"],
+ "next_hop": "10.0.12.1"
+ }]
+ },
+ "f1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.12.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.7.2"
+ },
+ {
+ "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.0.0/24", "10.0.4.0/24", "1.0.1.2/32"],
+ "next_hop": "10.0.3.1"
+ }]
+ },
+ "c1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "i4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.3.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.12.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.2.2"
+ },
+ {
+ "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"],
+ "next_hop": "10.0.0.2"
+ }]
+ },
+ "c2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.7.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.3.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.4.0/24", "10.0.2.0/24"],
+ "next_hop": "10.0.0.1"
+ }]
+ },
+ "i1": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "c1": {"ipv4": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "c2": {"ipv4": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i8": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
new file mode 100755
index 0000000..9228960
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo2/test_multicast_pim_sm_topo2.py
@@ -0,0 +1,1843 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+1. TC_17: Verify (*,G) and (S,G) present and multicast traffic resume,
+ after restart of PIMd daemon
+2. TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after
+ FRR service stop and start
+3. TC_10: Verify SPT switchover working when RPT and SPT path is
+ different
+4. TC_15: Verify (S,G) and (*,G) mroute after shut / no shut of upstream
+ interfaces
+5. TC_7: Verify mroute detail when receiver is present
+ outside of FRR
+6. TC_8: Verify mroute when FRR is acting as FHR and LHR
+7. TC_20: Verify mroute detail when 5 different receiver joining
+ same source
+8. TC_22: Verify OIL and IIF detail updated in (S,G) mroute after shut
+ and no shut of the source interface
+"""
+
+import os
+import sys
+import time
+import pytest
+
+pytestmark = pytest.mark.pimd
+
+# 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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router,
+ start_router_daemons,
+ stop_router,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_mroutes,
+ get_pim_interface_traffic,
+ verify_upstream_iif,
+ verify_pim_neighbors,
+ verify_pim_state,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.pimd]
+
+TOPOLOGY = """
+
+
+ i4-----c1-------------c2---i5
+ | |
+ | |
+ i1-----l1------r2-----f1---i2
+ | | | |
+ | | | |
+ i7 i6 i3 i8
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP
+ join and traffic
+ l1 - LHR
+ f1 - FHR
+ r2 - FRR router
+ c1 - FRR router
+ c2 - FRR router
+"""
+
+# Global variables
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_JOIN = "225.1.1.1"
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ json_file = "{}/multicast_pim_sm_topo2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # 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 verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_verify_mroute_and_traffic_when_pimd_restarted_p2(request):
+ """
+ TC_17: Verify (*,G) and (S,G) present and multicast traffic resume,
+ after restart of PIMd daemon
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c1")
+ step("Configure static RP for (232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ step(
+ "Configure IGMP interface on FRR3 and send IGMP join"
+ " for group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Connect one source to c2 and send multicast traffic all"
+ " the receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+ step(
+ "Send multicast traffic from FRR3 to all the receivers "
+ "(226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Verifying mroutes before PIMd restart, fetching uptime
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Restart Pimd process on FRR3 node")
+ kill_router_daemons(tgen, "f1", ["pimd"])
+ start_router_daemons(tgen, "f1", ["pimd"])
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After restart of PIMd verify pim nbr is up , IGMP groups"
+ " received , and (*,G) (S,G) entries populated again ,"
+ " Verify using 'show ip pim neighbor' , 'show ip igmp groups'"
+ " 'show ip mroute'"
+ )
+
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dut = "f1"
+ interface = "f1-i8-eth2"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic and restart PIMd immediately on FRR3 node")
+ dut = "i2"
+ intf = "i2-f1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ kill_router_daemons(tgen, "f1", ["pimd"])
+ start_router_daemons(tgen, "f1", ["pimd"])
+
+ step(
+ "After PIM process come , all the none of (S,G) mroute should"
+ " present on FRR3 'show ip mroute' "
+ )
+
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "none"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_and_traffic_when_frr_restarted_p2(request):
+ """
+ TC_18: Verify (*,G) and (S,G) present and multicast traffic resume after
+ FRR service stop and start
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c1")
+ step("Configure static RP for (232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ step(
+ "Configure IGMP interface on FRR3 and send IGMP join"
+ " for group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Connect one source to c2 and send multicast traffic all"
+ " the receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+ step(
+ "Send multicast traffic from FRR3 to all the receivers "
+ "(226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verifying mroutes before FRR restart, fetching uptime")
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop and Start the FRR services on FRR3 node")
+ stop_router(tgen, "f1")
+ start_router(tgen, "f1")
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After stop and start of FRR service verify pim nbr is up "
+ "IGMP groups received , and (*,G) (S,G) entries populated again"
+ " Verify using 'show ip pim neighbor' , 'show ip igmp groups'"
+ " 'show ip mroute'"
+ )
+
+ result = verify_pim_neighbors(tgen, topo)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dut = "f1"
+ interface = "f1-i8-eth2"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic and stop and start the FRR services on" " FRR3 node")
+ shutdown_bringup_interface(tgen, "i2", "i2-f1-eth0", False)
+
+ stop_router(tgen, "f1")
+ start_router(tgen, "f1")
+
+ step(
+ "After stop and start of FRR services , all the none of (S,G)"
+ " mroute should present on FRR3 node verify using "
+ "'show ip mroute'"
+ )
+
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "none"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_SPT_switchover_when_RPT_and_SPT_path_is_different_p0(request):
+ """
+ TC_10: Verify SPT switchover working when RPT and SPT path is
+ different
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) and " "(232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+
+ result = app_helper.run_join("i1", _IGMP_JOIN_RANGE, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to '226.1.1.1-5'" ", '232.1.1.1-5' receiver")
+
+ step("registerRx and registerStopTx value before traffic sent")
+ state_dict = {"c2": {"c2-f1-eth1": ["registerRx", "registerStopTx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \nError: {}".format(
+ tc_name, result
+ )
+
+ result = app_helper.run_traffic("i2", _IGMP_JOIN_RANGE, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify in FRR3 sending initial packet to RP using"
+ " 'show ip mroute' and mroute OIL is towards RP."
+ )
+
+ result = verify_mroutes(
+ tgen,
+ "f1",
+ "10.0.5.2",
+ _IGMP_JOIN_RANGE,
+ "f1-i2-eth1",
+ ["f1-c2-eth0", "f1-r2-eth3"],
+ )
+ assert result is True, "Testcase {} : " "Failed Error: {}".format(tc_name, result)
+
+ result = verify_mroutes(
+ tgen, "f1", "10.0.5.2", _IGMP_JOIN_RANGE, "f1-i2-eth1", "f1-r2-eth3"
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ " After spt switchover traffic is flowing between"
+ " (LHR(FRR1)-FHR(FRR3)) and (S,G) OIL is updated toward FRR1"
+ " 'show ip mroute' and 'show ip pim upstream'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic to all the receivers")
+
+ app_helper.stop_host("i2")
+
+ step(
+ "Null register packet being send periodically from FRR3 to RP, "
+ "verify using show ip mroute on RP, have (S, G) entries null OIL"
+ " 'show ip mroute' and verify show ip pim interface traffic"
+ "(In RP Register msg should be received and Register stop should"
+ " be transmitted)"
+ )
+ input_dict = [
+ {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "none"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("registerRx and registerStopTx value after traffic sent")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \nError: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_after_shut_noshut_of_upstream_interface_p1(request):
+ """
+ TC_15: Verify (S,G) and (*,G) mroute after shut / no shut of upstream
+ interfaces
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c1")
+ step("Configure static RP for (232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ step(
+ "Configure IGMP interface on FRR3 and send IGMP join"
+ " for group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Connect one source to c2 and send multicast traffic all"
+ " the receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+ step(
+ "Send multicast traffic from FRR3 to all the receivers "
+ "(226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_src = {"i2": "i2-f1-eth0", "i5": "i5-c2-eth0"}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "FRR3 (S,G) has one OIL for local receiver one toward c2"
+ " verify 'show ip mroute' and 'show ip pim upstream'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_2
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut interface connected from FHR (FRR3)" " to c2")
+ dut = "f1"
+ intf = "f1-c2-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("Shut and No shut interface connected from LHR (FRR1)" " to c1")
+ dut = "l1"
+ intf = "l1-c1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut FRR1 and FRR3 interface")
+ shutdown_bringup_interface(tgen, "l1", "l1-r2-eth4", False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ shutdown_bringup_interface(tgen, "f1", "f1-r2-eth3", False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After shut/no shut of interface , verify traffic resume to all"
+ "the receivers (S,G) OIL update for all the receivers"
+ )
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut FRR1, FRR3 interface , clear mroute in FRR1"
+ " and No shut FRR1, FRR3 interface "
+ )
+ dut = "l1"
+ intf = "l1-r2-eth4"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "f1"
+ intf = "f1-r2-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "l1"
+ intf = "l1-r2-eth4"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "f1"
+ intf = "f1-r2-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ clear_mroute(tgen, "l1")
+ clear_mroute(tgen, "l1")
+
+ step(
+ "After no shut, verify traffic resume to all the receivers"
+ " (S,G) OIL update for all the receivers"
+ )
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut and no shut upstream interface from FRR1 to FRR2 and "
+ "cisco immediate after mroute/upstream got cleared"
+ )
+
+ dut = "l1"
+ intf_l1_r2 = "l1-r2-eth4"
+ shutdown_bringup_interface(tgen, dut, intf_l1_r2, False)
+
+ intf_l1_c1 = "l1-c1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf_l1_c1, False)
+
+ result = verify_upstream_iif(
+ tgen, "l1", "Unknown", source, IGMP_JOIN_RANGE_2, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed Error: \n "
+ "mroutes are still present, after waiting for 10 mins".format(tc_name)
+ )
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, dut, intf_l1_r2, True)
+ shutdown_bringup_interface(tgen, dut, intf_l1_c1, True)
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic to all the receivers")
+ app_helper.stop_all_hosts()
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_receiver_is_outside_frr_p0(request):
+ """
+ TC_7: Verify mroute detail when receiver is present
+ outside of FRR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP on c1 for group range " "(226.1.1.1-5) and (232.1.1.1-5)")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join"
+ " (226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ result = app_helper.run_join("i1", _IGMP_JOIN_RANGE, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send multicast traffic from FRR3 to all the receivers "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ result = app_helper.run_traffic("i2", _IGMP_JOIN_RANGE, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure one more receiver in c2 enable IGMP and send"
+ " join (226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ input_dict = {
+ "c2": {
+ "igmp": {
+ "interfaces": {
+ "c2-i5-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i5", _IGMP_JOIN_RANGE, "c2")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("FRR1 has 10 (*.G) and 10 (S,G) verify using 'show ip mroute count'")
+ step(
+ "All the receiver are receiving traffic on FRR1 and (S,G) OIL is toward"
+ "receivers, verify using 'show ip mroute' 'show ip pim upstream'"
+ )
+ step(
+ "All the receiver are receiving traffic on c2 and (S,G) OIL is "
+ "toward receivers, verify using 'show ip mroute' 'show ip pim upstream'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "c2", "src_address": "*", "iif": "c2-c1-eth0", "oil": "c2-i5-eth2"},
+ {"dut": "c2", "src_address": source, "iif": "c2-f1-eth1", "oil": "c2-i5-eth2"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "FRR3 has (S,G) OIL created toward c1/c2 receiver and FRR1 receiver"
+ "'show ip pim state'"
+ )
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-c2-eth0"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ ]
+ for data in input_dict:
+ result = verify_pim_state(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["oil"],
+ _IGMP_JOIN_RANGE,
+ data["src_address"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_FRR_is_FHR_and_LHR_p0(request):
+ """
+ TC_8: Verify mroute when FRR is acting as FHR and LHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for group range (226.1.1.1-5) and " "(232.1.1.1-5) on c1")
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Enable IGMP on FRR1 interface and send IGMP join (226.1.1.1-5)"
+ " and (232.1.1.1-5)"
+ )
+ step(
+ "Configure receiver on FRR3 with igmp and pim enabled and "
+ "send IGMP join (226.1.1.1-5) and (232.1.1.1-5)"
+ )
+ step(
+ "Send multicast traffic from FRR3 to all the receivers "
+ "(226.1.1.1-5) and (232.1.1.1-5)"
+ )
+
+ step("Send IGMP join (226.1.1.1-5, 232.1.1.1-5) to LHR(l1)")
+ result = app_helper.run_join("i1", _IGMP_JOIN_RANGE, "l1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to '226.1.1.1-5'" ", '232.1.1.1-5' receiver")
+ result = app_helper.run_traffic("i2", _IGMP_JOIN_RANGE, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure receiver in f1 enable IGMP and send"
+ " join (226.1.1.1-5) and (232.1.1.1-5)"
+ )
+
+ step("Configure one IGMP interface on f1 node and send IGMP" " join (225.1.1.1)")
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = app_helper.run_join("i8", _IGMP_JOIN_RANGE, "f1")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+ step(
+ "l1 and f1 has 10 IGMP groups (226.1.1.1-5, 232.1.1.1-5),"
+ " verify using 'show ip igmp groups'"
+ )
+ dut = "l1"
+ interface = "l1-i1-eth1"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dut = "f1"
+ interface = "f1-i8-eth2"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "l1 , f1 has 10 (*,G) and 10 (S,G) for groups "
+ "(226.1.1.1-5, 232.1.1.1-5), verify using "
+ " 'show ip mroute'"
+ )
+
+ source = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {"dut": "f1", "src_address": source, "iif": "f1-i2-eth1", "oil": "f1-r2-eth3"},
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Join timer is running in FHR and LHR , verify using" " 'show ip pim state'")
+
+ for data in input_dict:
+ result = verify_pim_state(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["oil"],
+ _IGMP_JOIN_RANGE,
+ data["src_address"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Stop the multicast traffic
+ step("Stop the traffic to all the receivers")
+ app_helper.stop_all_hosts()
+
+ step(
+ "After traffic stopped , verify (*,G) entries are not flushed"
+ " out from FRR1 node verify using 'show ip mroute' "
+ )
+
+ input_dict = [
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed Error mroutes were flushed.".format(tc_name)
+
+ step(
+ "After traffic stopped , verify (S,G) entries are flushed out"
+ " from FRR1 node verify using 'show ip mroute' "
+ )
+
+ input_dict = [
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"},
+ {"dut": "f1", "src_address": source, "iif": "i2-f1-eth0", "oil": "f1-r2-eth3"},
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: \nmroutes are still present".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroute_when_5_different_receiver_joining_same_sources_p0(request):
+ """
+ TC_20: Verify mroute detail when 5 different receiver joining
+ same source
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c1")
+ step("Configure static RP for (232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure 2 IGMP interface on FRR1 and send IGMP join"
+ "for group (226.1.1.1-5, 232.1.1.1-5) from both the interface"
+ )
+ step(
+ "Configure 2 IGMP interface on FRR3 and send IGMP join for"
+ " group (226.1.1.1-5, 232.1.1.1-5) from both the interface"
+ )
+ step(
+ "Configure 1 IGMP interface on c2 and send IGMP join for"
+ "group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ },
+ "f1-i2-eth1": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ },
+ }
+ }
+ },
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i6-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ },
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {
+ "i1": "i1-l1-eth0",
+ "i6": "i6-l1-eth0",
+ "i8": "i8-f1-eth0",
+ "i2": "i2-f1-eth0",
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure one source in FRR2 , one in c1")
+ step(
+ "Send multicast traffic from both the sources to all the"
+ "receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_src = {"i3": "i3-r2-eth0"}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ step(
+ "After all the IGMP groups received with correct port using"
+ " 'show ip igmp groups' in FRR1, FRR3, c2"
+ )
+ dut = "l1"
+ interface = "l1-i6-eth2"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dut = "f1"
+ interface = "f1-i8-eth2"
+ result = verify_igmp_groups(tgen, dut, interface, _IGMP_JOIN_RANGE)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(*,G) entries got created with upstream interface RP connected"
+ " port using 'show ip pim upstream' in FRR1, FRR3, c2"
+ )
+ step(
+ "(S,G) entries created for all the receiver after starting the"
+ " source , traffic is reaching to all the receiver , verify OIL"
+ " of (S,G) is receiver port using 'show ip mroute' in FRR1, "
+ "FRR3 c2"
+ )
+
+ source = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": source,
+ "iif": ["l1-r2-eth4", "l1-c1-eth0"],
+ "oil": ["l1-i1-eth1", "l1-i6-eth2"],
+ },
+ {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"},
+ ]
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the receiver interface one by one on FRR1 node")
+ shutdown_bringup_interface(tgen, "l1", "l1-i1-eth1", False)
+ shutdown_bringup_interface(tgen, "l1", "l1-i6-eth2", False)
+
+ step(
+ "After shut the receiver port verify traffic is stopped immediately"
+ " and (S,G) got timeout immediately in FRR1, FRR3, c2"
+ )
+ input_dict = [
+ {"dut": "l1", "src_address": source, "iif": "l1-r2-eth4", "oil": "l1-i1-eth1"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "No traffic impact observed on other receivers verify using"
+ " 'show ip mroute' "
+ )
+ input_dict = [
+ {"dut": "f1", "src_address": source, "iif": "f1-r2-eth3", "oil": "f1-i8-eth2"}
+ ]
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("No shut the receiver interface one by one on FRR1 node")
+ shutdown_bringup_interface(tgen, "l1", "l1-i1-eth1", True)
+ shutdown_bringup_interface(tgen, "l1", "l1-i6-eth2", True)
+
+ step(
+ "After no shut of receivers all the mroute entries got populated"
+ ", no duplicate entries present in mroute"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_oil_iif_for_mroute_after_shut_noshut_source_interface_p1(request):
+ """
+ TC_22: Verify OIL and IIF detail updated in (S,G) mroute after shut
+ and no shut of the source interface
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Configure static RP for (226.1.1.1-5) in c1")
+ step("Configure static RP for (232.1.1.1-5) in c2")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_dict = {
+ "c1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ "c2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["c2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure IGMP interface on FRR1 and FRR3 and send IGMP join"
+ " for group (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ "f1-i8-eth2": {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i1": "i1-l1-eth0", "i8": "i8-f1-eth0"}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 1 source in FRR1 , 1 in FRR3")
+ step(
+ "Send multicast traffic from both the sources to all the "
+ "receivers (226.1.1.1-5, 232.1.1.1-5)"
+ )
+
+ input_src = {"i6": "i6-l1-eth0", "i2": "i2-f1-eth0"}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "*,G) is created and (S,G) created on FRR1 and FRR3 for both"
+ " the source verify using 'show ip mroute' and "
+ " 'show ip pim upstream' to check the upstream interface"
+ " details"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {"dut": "l1", "src_address": "*", "iif": "l1-c1-eth0", "oil": "l1-i1-eth1"},
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": "l1-r2-eth4",
+ "oil": "l1-i1-eth1",
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": "l1-i6-eth2",
+ "oil": "l1-i1-eth1",
+ },
+ {"dut": "f1", "src_address": "*", "iif": "f1-c2-eth0", "oil": "f1-i8-eth2"},
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": "f1-i2-eth1",
+ "oil": "f1-i8-eth2",
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i6,
+ "iif": "f1-r2-eth3",
+ "oil": "f1-i8-eth2",
+ },
+ ]
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the source interface one by one on FRR1")
+ shutdown_bringup_interface(tgen, "f1", "f1-i2-eth1", False)
+
+ step(
+ "After shut of ource interface from FRR3 verify all the (S,G) "
+ "entries flushed out from FRR3 node 'show ip pim upstream' "
+ " 'show ip mroute' "
+ )
+
+ result = verify_mroutes(
+ tgen,
+ "f1",
+ source_i2,
+ _IGMP_JOIN_RANGE,
+ "f1-i2-eth1",
+ "f1-i8-eth2",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are" " still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ result = verify_upstream_iif(
+ tgen, "f1", "Unknown", "10.0.5.2", _IGMP_JOIN_RANGE, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} : Failed 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/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json
new file mode 100644
index 0000000..89c54a4
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo3.json
@@ -0,0 +1,146 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "l1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i6": {"ipv4": "auto", "pim": "enable"},
+ "i7": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "query": {"query-max-response-time": 40, "query-interval": 5},
+ "version": "2"
+ }
+ }
+ }
+ },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.9.0/24", "1.0.3.5/32"],
+ "next_hop": "10.0.12.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "1.0.3.5/32", "10.0.1.0/24", "1.0.2.2/32", "10.0.4.0/24"],
+ "next_hop": "10.0.2.1"
+ }]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["10.0.5.0/24", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "1.0.3.5/32"],
+ "next_hop": "10.0.7.1"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.8.0/24", "10.0.10.0/24", "10.0.4.0/24", "10.0.11.0/24", "10.0.1.0/24"],
+ "next_hop": "10.0.12.1"
+ }]
+ },
+ "f1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24", "10.0.12.0/24"],
+ "next_hop": "10.0.7.2"
+ },
+ {
+ "network": ["1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24", "1.0.1.2/32"],
+ "next_hop": "10.0.3.1"
+ }]
+ },
+ "c1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "i4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.6.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.2.2"
+ },
+ {
+ "network": ["10.0.5.0/24", "10.0.7.0/24", "1.0.3.5/32", "10.0.6.0/24", "1.0.2.2/32", "10.0.1.0/24", "10.0.4.0/24"],
+ "next_hop": "10.0.0.2"
+ }]
+ },
+ "c2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.5.17/32", "10.0.5.0/24", "10.0.6.0/24", "10.0.7.0/24", "10.0.8.0/24", "10.0.9.0/24", "10.0.10.0/24", "10.0.11.0/24"],
+ "next_hop": "10.0.3.2"
+ },
+ {
+ "network": ["1.0.1.2/32", "10.0.4.0/24"],
+ "next_hop": "10.0.0.1"
+ }]
+ },
+ "i1": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "c1": {"ipv4": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "c2": {"ipv4": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i8": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json
new file mode 100644
index 0000000..afb5599
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/multicast_pim_sm_topo4.json
@@ -0,0 +1,143 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "l1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i6": {"ipv4": "auto", "pim": "enable"},
+ "i7": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1" :{
+ "igmp":{
+ "query": {"query-max-response-time": 40, "query-interval": 5},
+ "version": "2"
+ }
+ }
+ }
+ },
+ "static_routes": [{
+ "network": ["10.0.4.0/24", "10.0.3.1/24"],
+ "next_hop": "10.0.12.2"
+ },
+ {
+ "network": ["10.0.1.2/24"],
+ "next_hop": "10.0.2.1"
+ }]
+
+ },
+
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["10.0.4.0/24","10.0.3.1/24"],
+ "next_hop": "10.0.7.1"
+ },
+ {
+ "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"],
+ "next_hop": "10.0.12.1"
+ }]
+ },
+ "f1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"},
+ "i8": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["10.0.4.0/24","10.0.3.1/24"],
+ "next_hop": "10.0.3.1"
+ },
+ {
+ "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"],
+ "next_hop": "10.0.7.2"
+ }]
+ },
+ "c1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c2": {"ipv4": "auto", "pim": "enable"},
+ "l1": {"ipv4": "auto", "pim": "enable"},
+ "i4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [{
+ "network": ["1.0.4.11/32","10.0.4.2/24", "10.0.3.1/24"],
+ "next_hop": "10.0.2.2"
+ }]
+
+
+ },
+ "c2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "c1": {"ipv4": "auto", "pim": "enable"},
+ "f1": {"ipv4": "auto", "pim": "enable"},
+ "i5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": { "join-prune-interval": "5", "keep-alive-timer": 15, "register-suppress-time": 12 },
+ "static_routes": [
+ {
+ "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"],
+ "next_hop": "10.0.3.2"
+ }]
+ },
+ "i1": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "c1": {"ipv4": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "c2": {"ipv4": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "l1": {"ipv4": "auto"}
+ }
+ },
+ "i8": {
+ "links": {
+ "f1": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
new file mode 100755
index 0000000..b71c2d6
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo3.py
@@ -0,0 +1,4789 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. verify oil when join prune sent scenario_1 p0
+2. verify oil when join prune sent scenario_2 p0
+3. shut noshut source interface when upstream cleared from LHR p0(
+4. shut noshut receiver interface when upstream cleared from LHR p0(
+5. verify igmp clis p0
+6. verify igmp cli generate query once p0
+7. verify remove add igmp config to receiver interface p0
+8. verify remove add igmp commands when pim configured p0
+9. verify remove add pim commands when igmp configured p0
+10. pim dr priority p0
+11. pim hello timer p0
+12. Verify mroute after removing RP sending IGMP prune p2
+13. Verify prune is sent to LHR and FHR when PIM nbr went down
+14. Verify mroute flag in LHR and FHR node
+15. Verify IGMP prune processed correctly when same join received from IGMP and PIM
+16. Verify multicast traffic flowing fine, when LHR connected to RP
+17. Verify multicast traffic is flowing fine when FHR is connected to RP
+"""
+
+import os
+import re
+import sys
+import time
+import datetime
+import pytest
+from time import sleep
+
+pytestmark = pytest.mark.pimd
+
+# 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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ apply_raw_config,
+ check_router_status,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_mroutes,
+ clear_mroute,
+ clear_pim_interface_traffic,
+ verify_igmp_config,
+ verify_pim_config,
+ verify_pim_interface,
+ verify_upstream_iif,
+ verify_multicast_traffic,
+ verify_pim_rp_info,
+ verify_multicast_flag_state,
+ McastTesterHelper,
+ verify_pim_interface_traffic,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+pytestmark = pytest.mark.pimd
+
+TOPOLOGY = """
+
+ i4-----c1-------------c2---i5
+ | |
+ | |
+ i1-----l1------r2-----f1---i2
+ | | | |
+ | | | |
+ i7 i6 i3 i8
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP
+ join and traffic
+ l1 - LHR
+ f1 - FHR
+ r2 - FRR router
+ c1 - FRR router
+ c2 - FRR router
+"""
+
+# Global variables
+VLAN_1 = 2501
+GROUP_RANGE = "225.0.0.0/8"
+IGMP_GROUP = "225.1.1.1/32"
+IGMP_JOIN = "225.1.1.1"
+VLAN_INTF_ADRESS_1 = "10.0.8.3/24"
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+SAME_VLAN_IP_1 = {"ip": "10.1.1.1", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_2 = {"ip": "10.1.1.2", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_3 = {"ip": "10.1.1.3", "subnet": "255.255.255.0", "cidr": "24"}
+SAME_VLAN_IP_4 = {"ip": "10.1.1.4", "subnet": "255.255.255.0", "cidr": "24"}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ json_file = "{}/multicast_pim_sm_topo3.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # 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 verify_mroute_repopulated(uptime_before, uptime_after):
+ """
+ API to compare uptime for mroutes
+
+ Parameters
+ ----------
+ * `uptime_before` : Uptime dictionary for any particular instance
+ * `uptime_after` : Uptime dictionary for any particular instance
+ """
+
+ for group in uptime_before.keys():
+ for source in uptime_before[group].keys():
+ if set(uptime_before[group]) != set(uptime_after[group]):
+ errormsg = (
+ "mroute (%s, %s) has not come"
+ " up after mroute clear [FAILED!!]" % (source, group)
+ )
+ return errormsg
+
+ d1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S")
+ d2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S")
+ if d2 >= d1:
+ errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % (
+ source,
+ group,
+ )
+ return errormsg
+
+ logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group)
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for ttype, v1 in state_before.items():
+ for intf, v2 in v1.items():
+ for state, value in v2.items():
+ if value >= state_after[ttype][intf][state]:
+ errormsg = "[DUT: %s]: state %s value has not incremented, Initial value: %s, Current value: %s [FAILED!!]" % (
+ intf,
+ state,
+ value,
+ state_after[ttype][intf][state],
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is incremented, Initial value: %s, Current value: %s [PASSED!!]",
+ intf,
+ state,
+ value,
+ state_after[ttype][intf][state],
+ )
+
+ return True
+
+
+def find_v2_query_msg_in_tcpdump(tgen, router, message, count, cap_file):
+ """
+ Find v2 query messages in tcpdump file
+
+ Parameters
+ ----------
+ * `tgen` : Topology handler
+ * `router` : Device under test
+ * `cap_file` : tcp dump file name
+
+ """
+
+ filepath = os.path.join(tgen.logdir, router, cap_file)
+ with open(filepath) as f:
+ if len(re.findall("{}".format(message), f.read())) < count:
+ errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % (
+ router,
+ message,
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: Found message: %s in tcpdump " " count: %s [PASSED!!]",
+ router,
+ message,
+ count,
+ )
+ return True
+
+
+def find_tos_in_tcpdump(tgen, router, message, cap_file):
+ """
+ Find v2 query messages in tcpdump file
+
+ Parameters
+ ----------
+ * `tgen` : Topology handler
+ * `router` : Device under test
+ * `cap_file` : tcp dump file name
+
+ """
+
+ filepath = os.path.join(tgen.logdir, router, cap_file)
+ with open(filepath) as f:
+
+ if len(re.findall(message, f.read())) < 1:
+ errormsg = "[DUT: %s]: Verify Message: %s in tcpdump" " [FAILED!!]" % (
+ router,
+ message,
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: Found message: %s in tcpdump " "[PASSED!!]", router, message
+ )
+ return True
+
+
+def verify_pim_stats_increament(stats_before, stats_after):
+ """
+ API to compare pim interface control plane traffic
+
+ Parameters
+ ----------
+ * `stats_before` : Stats dictionary for any particular instance
+ * `stats_after` : Stats dictionary for any particular instance
+ """
+
+ for router, stats_data in stats_before.items():
+ for stats, value in stats_data.items():
+ if stats_before[router][stats] >= stats_after[router][stats]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ stats,
+ stats_before[router][stats],
+ stats_after[router][stats],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ stats,
+ stats_before[router][stats],
+ stats_after[router][stats],
+ )
+
+ return True
+
+
+def test_verify_oil_when_join_prune_sent_scenario_1_p1(request):
+ """
+ TC_21_1:
+ Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP
+ join/prune is sent
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (226.1.1.1-5)"
+ )
+ step(
+ "Enable IGMP of FRR3 interface and send IGMP joins "
+ " from FRR3 node for group range (226.1.1.1-5)"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ intf_f1_i8: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["l1"]["interface"],
+ "i8": topo["routers"]["i8"]["links"]["f1"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (226.1.1.1-5) in R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure one source on FRR3 for all the groups and send" " multicast traffic"
+ )
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1 node")
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "After receiving the IGMP prune from FRR1 , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ input_traffic = {"l1": {"traffic_sent": [intf_l1_i1]}}
+ result = verify_multicast_traffic(tgen, input_traffic, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " " Traffic is not stopped yet \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "IGMP groups are remove from FRR1 node 'show ip igmp groups'"
+ " FRR3 IGMP still present"
+ )
+
+ dut = "l1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_l1_i1, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "IGMP groups are not deleted \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ dut = "f1"
+ result = verify_igmp_groups(tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(*,G) and (S,G) OIL got removed immediately after receiving"
+ " prune 'show ip pim state' and 'show ip mroute' on FRR1 node,"
+ " no impact on FRR3 receiver"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ input_dict_f1 = [
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_f1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_f1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " " FRR3 node")
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i8, False)
+
+ step(
+ "After receiving the IGMP prune from FRR3s , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ result = verify_multicast_traffic(tgen, input_traffic, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " " Traffic is not stopped yet \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "IGMP groups are remove from FRR1 node 'show ip igmp groups'"
+ " FRR3 IGMP still present"
+ )
+
+ dut = "f1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "IGMP groups are not deleted \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "(*,G) and (S,G) OIL got prune state (none) from all the nodes"
+ "FRR1, FRR3 verify using 'show ip mroute'"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i8, True)
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_oil_when_join_prune_sent_scenario_2_p1(request):
+ """
+ TC_21_2: Verify OIL detail updated in (S,G) and (*,G) mroute when IGMP
+ join/prune is sent
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Removing FRR3 to simulate topo " "FHR(FRR1)---LHR(FRR2)")
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_r2, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (226.1.1.1-5)"
+ )
+ step(
+ "Enable IGMP of FRR3 interface and send IGMP joins "
+ " from FRR3 node for group range (226.1.1.1-5)"
+ )
+
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ input_dict = {
+ "r2": {
+ "igmp": {
+ "interfaces": {
+ intf_r2_i3: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["l1"]["interface"],
+ "i3": topo["routers"]["i3"]["links"]["r2"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (226.1.1.1-5) in R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR3(r2) node")
+
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False)
+
+ step(
+ "After sending IGMP prune from FRR3(r2) node verify (*,G) OIL "
+ "immediately removed for local receiver mroute should have "
+ " PIM protocol , IGMP should be removed verify using "
+ "'show ip mroute' no impact seen on FRR1(l1) (*,G)"
+ )
+
+ input_dict_r2 = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ }
+ ]
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ input_dict_l1_r2 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_l1_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send the IGMP prune from ixia to (226.1.1.1-5) receiver on " "FRR1(l1) node")
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "After sending IGMP prune from FRR1 node verify (*,G) OIL"
+ "got removed immediately from FRR1 node"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("After prune is sent verify upstream got removed in FRR1 node")
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream entries are still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_shut_noshut_source_interface_when_upstream_cleared_from_LHR_p1(request):
+ """
+ TC_26: Verify shut/no shut of source interface after upstream got cleared
+ from LHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver")
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ip mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ step(
+ "'show ip pim upstream' and 'show ip pim upstream-rpf' showing"
+ " correct OIL and IIF on all the nodes"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the source interface from FRR3")
+ intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"]
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False)
+
+ step(
+ "After shut of source interface verify (S,G) mroutes are cleared"
+ " from all the nodes"
+ )
+
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ result = verify_mroutes(
+ tgen, "f1", source_i2, IGMP_JOIN_RANGE_1, intf_f1_i2, intf_f1_r2, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n mroutes are" " still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behavior: {}".format(result))
+
+ step(
+ "After waiting for (S,G) timeout from FRR1 for same"
+ " source verify that (S,G) is flushed from FRR1 node"
+ " 'show ip pim upstream' 'show ip mroute' "
+ )
+
+ result = verify_upstream_iif(
+ tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: \n mroutes are still present".format(tc_name)
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True)
+
+ step(
+ "After no shut of source interface , verify all the (S,G) is "
+ " populated again on 'show ip mroute' 'show ip pim upstream' "
+ " with proper OIL and IIF detail"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("shut and no shut the source interface immediately")
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True)
+
+ step(
+ "All the mroutes got updated with proper OIL after no shut of"
+ "interface verify using 'show ip mroute'"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_shut_noshut_receiver_interface_when_upstream_cleared_from_LHR_p1(request):
+ """
+ TC_27: Verify shut/no shut of receiver interface after upstream got
+ cleared from LHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP on R2 (loopback interface) for " "the group range 225.0.0.0/8")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver")
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ip mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ip pim upstream' and 'show ip pim upstream-rpf' showing"
+ " correct OIL and IIF on all the nodes"
+ )
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the source interface FRR1")
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "After waiting for (S,G) timeout from FRR1 for same"
+ " source verify that (S,G) is flushed from FRR1 node"
+ " 'show ip pim upstream' 'show ip mroute' "
+ )
+
+ result = verify_upstream_iif(
+ tgen, "l1", "Unknown", source_i2, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed Error: \nmroutes are still present".format(tc_name)
+
+ step("No shut the Source interface just after the upstream is expired" " from FRR1")
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ step(
+ "After no shut of source interface , verify all the (S,G) is "
+ " populated again on 'show ip mroute' 'show ip pim upstream' "
+ " with proper OIL and IIF detail"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("shut and no shut the source interface immediately")
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_i2, True)
+
+ step(
+ "After no shut of receiver interface , verify all the (S,G) is "
+ "populated again on 'show ip mroute' 'show ip pim upstream' "
+ "with proper OIL and IIF detail"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_remove_add_igmp_config_to_receiver_interface_p0(request):
+ """
+ TC_33: Verify removing and adding IGMP config from the receiver interface
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Enable PIM on all routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)")
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)")
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'")
+
+ dut = "l1"
+ interface = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Groups are not" " present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ intf_f1_i2 = topo["routers"]["f1"]["links"]["i2"]["interface"]
+ input_traffic = {
+ "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]},
+ "f1": {"traffic_sent": [intf_f1_r2], "traffic_received": [intf_f1_i2]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure igmp 'ip igmp' and 'ip igmp version 2' from "
+ "receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After adding IGMP on receiver interface verify (S,G) and (*,G)"
+ " entries got populated and traffic is resumed on FRR1 and FRR3 node"
+ )
+
+ step(
+ "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic"
+ " using show ip pim upstream and show ip multicast'"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("IGMP join removed from FRR1 , verify using " "'show ip igmp groups json'")
+
+ dut = "l1"
+ interface = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, dut, interface, IGMP_JOIN_RANGE_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Groups are not" " present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure igmp 'ip igmp' and 'ip igmp version 2' from "
+ "receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After adding IGMP on receiver interface verify (S,G) and (*,G)"
+ " entries got populated and traffic is resumed on FRR1 and FRR3 node"
+ )
+
+ step(
+ "Verify OIL/IIF and drJoinDesired using 'show ip mroute , and traffic"
+ " using show ip pim upstream and show ip multicast'"
+ )
+
+ input_dict_l1_f1 = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_l1_f1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_l1_f1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Remove ip igmp and send igmp prune from FRR1 interface")
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+ step(
+ "Verification: After removing igmp 'no ip igmp' and "
+ " sending prune verify mroute and upstream got removed"
+ " from FRR1 verify using 'show ip mroute' and "
+ "'show ip pim upstream'"
+ )
+
+ dut = "l1"
+ iif = topo["routers"]["l1"]["links"]["i6"]["interface"]
+ oil = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ source = source_i6
+ result = verify_mroutes(
+ tgen, dut, source, IGMP_JOIN_RANGE_1, iif, oil, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n routes are still" " present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_verify_remove_add_igmp_commands_when_pim_configured_p0(request):
+ """
+ TC_34: Verify removing and adding IGMP commands when PIM is already
+ configured
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Enable PIM on all routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure source on FRR3 and start the traffic for" " (225.1.1.1-225.1.1.10)")
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure source on FRR1 and start the traffic for" " (225.1.1.1-225.1.1.10)")
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i6 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i6,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After configuring IGMP related config , "
+ "verify config is present in the interface "
+ "'show ip igmp interface ensxx json'"
+ )
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ input_dict_1 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {
+ "version": "2",
+ "query": {
+ "query-max-response-time": 40,
+ "query-interval": 5,
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = verify_igmp_config(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Remove igmp 'no ip igmp' and 'no ip igmp version 2' from"
+ " receiver interface of FRR1"
+ )
+
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ intf_l1_i1: {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After removing the config CLI got removed "
+ "'show ip igmp interface ensxx json'"
+ )
+
+ result = verify_igmp_config(tgen, input_dict_1, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "IGMP interface is not removed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 5}}}
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "query": {"last-member-query-count": "", "delete": True}
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {"igmp": {"query": {"last-member-query-count": 2}}}
+ }
+ }
+ }
+ }
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Configure 'ip igmp last-member-query-interval 20' on FRR1"
+ " receiver interface"
+ )
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {"query": {"last-member-query-interval": 20}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'ip igmp last-member-query-count 10' on FRR1" " receiver interface")
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "query": {"last-member-query-interval": "", "delete": True}
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict_3 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {"query": {"last-member-query-interval": 10}}
+ }
+ }
+ }
+ }
+ }
+ result = verify_igmp_config(tgen, input_dict_3)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_remove_add_pim_commands_when_igmp_configured_p1(request):
+ """
+ TC_35: Verify removing and adding PIM commands when IGMP is already
+ configured
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Configure 'ip pim' on receiver interface on FRR1")
+ step("Enable PIM on all routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim' on receiver interface on FRR1")
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim bsm' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim bsm"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim bsm' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim bsm"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim drpriority' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {
+ "raw_config": ["interface {}".format(intf_l1_i1), "ip pim drpriority 10"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After configuring PIM related config, "
+ "verify config is present in the interface "
+ "'show ip pim interface ensxx json'"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 10}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim drpriority' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {
+ "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim drpriority 10"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After removing the config CLI got removed "
+ "'show ip pim interface ensxx json'"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"drPriority": 1}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim hello' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim hello 50"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After configuring PIM related config, "
+ "verify config is present in the interface "
+ "'show ip pim interface ensxx json'"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 50}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim hello' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "no ip pim hello"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verification: After removing the config CLI got removed "
+ "'show ip pim interface ensxx json'"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_i1: {"helloPeriod": 30}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim unicast-bsm' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_i1), "ip pim unicast-bsm"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim hello' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {
+ "raw_config": ["interface {}".format(intf_l1_i1), "no ip pim unicast-bsm"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim_dr_priority_p0(request):
+ """
+ TC_36: Verify highest DR priority become the PIM DR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Configure 'ip pim' on receiver interface on FRR1")
+ step("Enable PIM on all routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_src = {"i2": topo["routers"]["i2"]["links"]["f1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim drpriority 10' on receiver interface on FRR1(LHR)")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ raw_config = {
+ "l1": {
+ "raw_config": ["interface {}".format(intf_l1_r2), "ip pim drpriority 10"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "DR config is successful on FRR1 node , verify using "
+ " 'show ip pim interface json'"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 10}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure 'ip pim drpriority 20' on receiver interface on FRR3(FHR)")
+
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ raw_config = {
+ "f1": {
+ "raw_config": ["interface {}".format(intf_f1_r2), "ip pim drpriority 20"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "DR config is successful on FRR3 node , verify using "
+ " 'show ip pim interface json'"
+ )
+
+ input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 20}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "PIM is enable on FRR1, FRR2 interface and neighbor is up, "
+ " verify using 'show ip pim interface'"
+ )
+
+ result = verify_pim_interface(tgen, topo, "l1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_pim_interface(tgen, topo, "f1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Highet IP become PIM DR , verify using "
+ "'show ip pim interface json' and 'show ip pim neighbor'"
+ )
+ step("Highest priority become PIM DR")
+
+ dr_address = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict_dr = {
+ "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dr_address = topo["routers"]["f1"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict_dr = {
+ "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim drpriority' on receiver interface on FRR1")
+
+ raw_config = {
+ "l1": {
+ "raw_config": ["interface {}".format(intf_l1_r2), "no ip pim drpriority 10"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove 'no ip pim drpriority' on receiver interface on FRR3")
+
+ raw_config = {
+ "f1": {
+ "raw_config": ["interface {}".format(intf_f1_r2), "no ip pim drpriority 20"]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After removing drpriority , config got removed from both the "
+ "nodes and highest IP become PIM DR"
+ )
+
+ input_dict_dr = {"l1": {"pim": {"interfaces": {intf_l1_r2: {"drPriority": 1}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict_dr = {"f1": {"pim": {"interfaces": {intf_f1_r2: {"drPriority": 1}}}}}
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dr_address = topo["routers"]["r2"]["links"]["l1"]["ipv4"].split("/")[0]
+ input_dict_dr = {
+ "l1": {"pim": {"interfaces": {intf_l1_r2: {"drAddress": dr_address}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dr_address = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0]
+ input_dict_dr = {
+ "f1": {"pim": {"interfaces": {intf_f1_r2: {"drAddress": dr_address}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_dr)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_pim_hello_timer_p1(request):
+ """
+ TC_37: Verify PIM hello is sent on configured timer
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step("Configure 'ip pim' on receiver interface on FRR1")
+ step("Enable PIM on all routers")
+ step("Enable IGMP on FRR1 interface and send IGMP join " "(225.1.1.1-225.1.1.10)")
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in cisco-1(f1)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure PIM hello interval timer 100 on FRR1 node (FRR1-FRR2 link)")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 100"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "PIM hello interval is configured on interface verify using "
+ "'show ip pim interface'"
+ )
+
+ input_dict_hello = {
+ "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 100}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_hello)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Modify hello timer to 180 and then 50sec")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 180"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "PIM hello interval is configured on interface verify using "
+ "'show ip pim interface'"
+ )
+
+ input_dict_hello = {
+ "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 180}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_hello)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ raw_config = {
+ "l1": {"raw_config": ["interface {}".format(intf_l1_r2), "ip pim hello 50"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "PIM hello interval is configured on interface verify using "
+ "'show ip pim interface'"
+ )
+
+ input_dict_hello = {
+ "l1": {"pim": {"interfaces": {intf_l1_r2: {"helloPeriod": 50}}}}
+ }
+ result = verify_pim_config(tgen, input_dict_hello)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify that no core is observed")
+ if tgen.routers_have_failure():
+ assert False, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_after_removing_RP_sending_IGMP_prune_p2(request):
+ """
+ TC_39 Verify mroute after removing the RP and sending IGMP prune
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step(
+ "Remove cisco connected link to simulate topo "
+ "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))"
+ )
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ intf_f1_i8: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send"
+ " multicast traffic"
+ )
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["l1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Remove the RP config for both the range from all the nodes")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_dict_starg = [
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroute still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send prune from receiver-1 (using ctrl+c) on iperf interface")
+ app_helper.stop_all_hosts()
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ traffic_before = verify_multicast_traffic(tgen, input_traffic, return_traffic=True)
+ assert isinstance(traffic_before, dict), (
+ "Testcase {} : Failed \n traffic_before is not dictionary \n "
+ "Error: {}".format(tc_name, result)
+ )
+
+ step("IGMP groups are remove from FRR1 node 'show ip igmp groups'")
+
+ dut = "f1"
+ result = verify_igmp_groups(
+ tgen, dut, intf_f1_i8, IGMP_JOIN_RANGE_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "IGMP groups still present still present \n Error: {}".format(tc_name, result)
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step(
+ "After receiving the IGMP prune from FRR1 , verify traffic "
+ "immediately stopped for this receiver 'show ip multicast'"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ traffic_after = verify_multicast_traffic(tgen, input_traffic, return_traffic=True)
+ assert isinstance(traffic_after, dict), (
+ "Testcase {} : Failed \n traffic_after is not dictionary \n "
+ "Error: {}".format(tc_name, result)
+ )
+
+ result = verify_state_incremented(traffic_before, traffic_after)
+ assert result is not True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Configure static RP for (225.1.1.1-5) as R2 loopback interface")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins again from LHR,check IGMP joins and starg received")
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from FHR and verify mroute upstream")
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_prune_sent_to_LHR_and_FHR_when_PIMnbr_down_p2(request):
+ """
+ TC_38 Verify prune is sent to LHR and FHR when PIM nbr went down
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step(
+ "Remove cisco connected link to simulate topo "
+ "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))"
+ )
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ intf_f1_i8: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send"
+ " multicast traffic"
+ )
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["l1"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["f1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i1,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+ step("Verify mcast traffic received")
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the link from LHR to RP from RP node")
+
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False)
+
+ step("Verify RP info after Shut the link from LHR to RP from RP node")
+ dut = "f1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_starg = [
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ }
+ ]
+
+ input_dict_sg_i2 = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ input_dict_sg_i1 = [
+ {
+ "dut": "f1",
+ "src_address": source_i1,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ }
+ ]
+
+ input_dict_sg_i2_l1 = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ }
+ ]
+
+ step("Verify mroute after Shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroute still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstream after Shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "upstream still present \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("No shut the link from LHR to RP from RP node")
+
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True)
+
+ step("Verify RP info after No shut the link from LHR to RP from RP node")
+ dut = "f1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "RP iif is not updated \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after No shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstrem after No shut the link from LHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify mcast traffic received after noshut LHR to RP from RP node")
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the link from FHR to RP from RP node")
+
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_l1, False)
+
+ step("Verify RP info after Shut the link from FHR to RP from RP node")
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroute after Shut the link from FHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstream after Shut the link from FHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream is still present after shut the link from "
+ "FHR to RP from RP node \n Error: {}".format(tc_name, result)
+ )
+
+ step(" No shut the link from FHR to RP from RP node")
+
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_l1, True)
+
+ step("Verify RP info after Noshut the link from FHR to RP from RP node")
+
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "RP iif is not updated \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after Noshut the link from FHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify mroute after Noshut the link from FHR to RP from RP node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify mcast traffic received after noshut FHR to RP from RP node")
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the link from FHR to RP from FHR node")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False)
+
+ step("Verify PIM Nbrs after Shut the link from FHR to RP from FHR node")
+
+ step("Verify RP info after Shut the link from FHR to RP from FHR node")
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroute after Shut the link from FHR to RP from FHR node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstream after Shut the link from FHR to RP from FHR node")
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2_l1:
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream is still present after shut the link from "
+ "FHR to RP from FHR node \n Error: {}".format(tc_name, result)
+ )
+
+ step(" No shut the link from FHR to RP from FHR node")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True)
+
+ step("Verify RP info after No Shut the link from FHR to RP from FHR node")
+ dut = "l1"
+ rp_address = "1.0.5.17"
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "RP iif is not updated \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("Verify mroute after No Shut the link from FHR to RP from FHR node")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify upstream after No Shut the link from FHR to RP from FHR node")
+
+ for data in input_dict_starg:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg_i2:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify mcast traffic received after noshut FHR to RP from FHR node")
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_traffic = {"f1": {"traffic_sent": [intf_f1_i8]}}
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_flags_p1(request):
+ """
+ TC_47 Verify mroute flag in LHR and FHR node
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step(
+ "Remove cisco connected link to simulate topo "
+ "LHR(FRR1(f1))----RP(cisco(f1)---FHR(FRR3(l1))"
+ )
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_f1_i8 = topo["routers"]["f1"]["links"]["i8"]["interface"]
+ input_dict = {
+ "f1": {
+ "igmp": {
+ "interfaces": {
+ intf_f1_i8: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i8": topo["routers"]["i8"]["links"]["f1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send traffic from FHR to all the groups ( 225.1.1.1 to 225.1.1.5) and send"
+ " multicast traffic"
+ )
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["l1"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["f1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i2 = topo["routers"]["i6"]["links"]["l1"]["ipv4"].split("/")[0]
+ source_i1 = topo["routers"]["i2"]["links"]["f1"]["ipv4"].split("/")[0]
+
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["l1"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i1,
+ "iif": topo["routers"]["f1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["f1"]["links"]["i8"]["interface"],
+ },
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ dut = "f1"
+ step("verify flag for (*,G) on f1")
+ src_address = "*"
+ flag = "SC"
+ result = verify_multicast_flag_state(
+ tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify flag for (S,G) on f1 for Remote spurce ")
+ src_address = source_i2
+ flag = "ST"
+ result = verify_multicast_flag_state(
+ tgen, dut, src_address, IGMP_JOIN_RANGE_1, flag
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_multicast_traffic_when_LHR_connected_to_RP_p1(request):
+ """
+ TC_11: Verify multicast traffic flowing fine, when LHR connected to RP
+ Topology used:
+ FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1))
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step(
+ "Remove FRR3 to cisco connected link to simulate topo "
+ "FHR(FRR3(l1))---LHR(FRR1(r2)----RP(FRR2(f1))"
+ )
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+
+ step("Disable IGMP config from l1")
+ input_dict_2 = {
+ "l1": {
+ "igmp": {
+ "interfaces": {
+ "l1-i1-eth1": {
+ "igmp": {
+ "version": "2",
+ "delete": True,
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers")
+ step(
+ "Enable IGMP on FRR1(r2) interface and send IGMP join (226.1.1.1-5)"
+ " and (232.1.1.1-5)"
+ )
+
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ input_dict = {
+ "r2": {
+ "igmp": {
+ "interfaces": {
+ intf_r2_i3: {
+ "igmp": {"version": "2", "query": {"query-interval": 15}}
+ }
+ }
+ }
+ }
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_join = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (226.1.1.1-5) and (232.1.1.1-5) in (f1)")
+
+ input_dict = {
+ "f1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3 to 225.1.1.1-225.1.1.10" " receiver")
+
+ input_src = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ip mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+
+ source_i1 = topo["routers"]["i1"]["links"]["l1"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": source_i1,
+ "iif": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i1,
+ "iif": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic is flowing for all the groups verify"
+ "using 'show ip multicast'"
+ )
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ intf_r2_l1 = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ input_traffic = {
+ "l1": {"traffic_received": [intf_l1_i1]},
+ "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut the receiver port")
+
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False)
+
+ step(
+ "Verification: After Shut of receiver port, Verify (*,G) and "
+ "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'"
+ )
+
+ input_dict_r2 = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i1,
+ "iif": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ ]
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n"
+ " Expected Behaviour: mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True)
+
+ step(
+ "Verification: After No shut of receiver port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_r2:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic is resumed for all the groups verify "
+ "using 'show ip multicast'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut the source port")
+
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "Verification: After Shut of source port, Verify (*,G) and "
+ "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": source_i1,
+ "iif": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ }
+ ]
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic is resumed for all the groups verify "
+ "using 'show ip multicast'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and No shut of LHR to cisco port from LHR side")
+
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False)
+
+ step(
+ "Verification: After Shut of source port, Verify (S,G) got "
+ "removed from LHR and FHR using 'show ip mroute'"
+ )
+
+ input_dict_r2_f1 = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_r2_f1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" " mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic is resumed for all the groups verify "
+ "using 'show ip multicast'"
+ )
+
+ input_traffic_r2 = {
+ "r2": {"traffic_received": [intf_r2_l1], "traffic_sent": [intf_r2_i3]}
+ }
+ result = verify_multicast_traffic(tgen, input_traffic_r2)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and no shut of FHR to LHR port from FHR side")
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, False)
+
+ step(
+ "Verification: After Shut of LHR to FHR port, Verify (S,G)"
+ "got removed from LHR 'show ip mroute'"
+ )
+
+ dut = "r2"
+ src_address = "*"
+ iif = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ oil = topo["routers"]["r2"]["links"]["i3"]["interface"]
+
+ result = verify_mroutes(tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ src_address = source_i1
+ iif = topo["routers"]["r2"]["links"]["l1"]["interface"]
+ oil = topo["routers"]["r2"]["links"]["i3"]["interface"]
+
+ result = verify_mroutes(
+ tgen, dut, src_address, _IGMP_JOIN_RANGE, iif, oil, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" " mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_r2, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic is resumed for all the groups verify "
+ "using 'show ip multicast'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic_r2)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_verify_multicast_traffic_when_FHR_connected_to_RP_p1(request):
+ """
+ TC_12: Verify multicast traffic is flowing fine when FHR is connected to RP
+ Topology used:
+ LHR(FRR1)---FHR(FRR3)----RP(FRR2)
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+ check_router_status(tgen)
+
+ step(
+ "Remove FRR3 to FRR2 connected link to simulate topo "
+ "FHR(FRR3)---LHR(FRR1)----RP(FFR2)"
+ )
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["interface"]
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+ shutdown_bringup_interface(tgen, "f1", intf_f1_c2, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, R2 and FRR3" " routers")
+ step("Enable IGMP on FRR1(l1) interface and send IGMP join " " and (225.1.1.1-5)")
+
+ _GROUP_RANGE = GROUP_RANGE_2 + GROUP_RANGE_3
+ _IGMP_JOIN_RANGE = IGMP_JOIN_RANGE_2 + IGMP_JOIN_RANGE_3
+
+ input_join = {"i1": topo["routers"]["i1"]["links"]["l1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, _IGMP_JOIN_RANGE, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP for (225.1.1.1-5) in (f1)")
+
+ input_dict = {
+ "f1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["f1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": _GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send multicast traffic from FRR3(r2) to 225.1.1.1-225.1.1.10" " receiver")
+
+ input_src = {"i3": topo["routers"]["i3"]["links"]["r2"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, _IGMP_JOIN_RANGE, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "'show ip mroute' showing correct RPF and OIF interface for (*,G)"
+ " and (S,G) entries on all the nodes"
+ )
+
+ source_i3 = topo["routers"]["i3"]["links"]["r2"]["ipv4"].split("/")[0]
+ input_dict_all = [
+ {
+ "dut": "l1",
+ "src_address": "*",
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "r2",
+ "src_address": source_i3,
+ "iif": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_all:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["interface"]
+ intf_f1_r2 = topo["routers"]["f1"]["links"]["r2"]["interface"]
+ intf_l1_i1 = topo["routers"]["l1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "l1": {"traffic_received": [intf_l1_r2], "traffic_sent": [intf_l1_i1]}
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the receiver(l1) port in 1 min interval")
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, False)
+
+ step(
+ "Verification: After Shut of receiver port, Verify (*,G) and "
+ "(S,G) got removed from LHR node (FRR1) using 'show ip mroute'"
+ )
+
+ input_dict_l1 = [
+ {
+ "dut": "l1",
+ "src_address": source_i3,
+ "iif": topo["routers"]["l1"]["links"]["r2"]["interface"],
+ "oil": topo["routers"]["l1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" " mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("No shut the receiver(l1) port in 1 min interval")
+
+ shutdown_bringup_interface(tgen, "l1", intf_l1_i1, True)
+
+ step(
+ "Verification: After No shut of receiver port , Verify (*,G)"
+ " and (S,G) got populated on LHR node (FRR1) using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_l1:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_l1:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the source(r2) port in 1 min interval")
+
+ intf_r2_i3 = topo["routers"]["r2"]["links"]["i3"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, False)
+
+ step(
+ "Verification: After Shut of source port, Verify (S,G) got "
+ "removed from FHR using 'show ip mroute'"
+ )
+
+ input_dict_r2 = [
+ {
+ "dut": "r2",
+ "src_address": source_i3,
+ "iif": topo["routers"]["r2"]["links"]["i3"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" " mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ step("No shut the source(r2) port in 1 min interval")
+
+ shutdown_bringup_interface(tgen, "r2", intf_r2_i3, True)
+
+ step(
+ "Verification: After No shut of source port , Verify (*,G)"
+ " and (S,G) got populated on LHR and FHR using "
+ "'show ip mroute' 'show ip pim upstream'"
+ )
+
+ for data in input_dict_r2:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_r2:
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], _IGMP_JOIN_RANGE
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut FHR to RP port from FHR side")
+
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["interface"]
+ shutdown_bringup_interface(tgen, "r2", intf_r2_f1, False)
+
+ step(
+ "Verification: After Shut of FHR to cisco port, Verify (*,G) "
+ "got removed from FHR and cisco node using 'show ip mroute'"
+ )
+
+ input_dict_all_star = [
+ {
+ "dut": "r2",
+ "src_address": "*",
+ "iif": topo["routers"]["r2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["r2"]["links"]["l1"]["interface"],
+ },
+ {
+ "dut": "f1",
+ "src_address": "*",
+ "iif": "lo",
+ "oil": topo["routers"]["f1"]["links"]["r2"]["interface"],
+ },
+ ]
+
+ for data in input_dict_all_star:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ _IGMP_JOIN_RANGE,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" " mroutes are cleared \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Expected Behaviour: {}".format(result))
+
+ write_test_footer(tc_name)
+
+
+def test_PIM_passive_p1(request):
+ """
+ TC Verify PIM passive functionality"
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ app_helper.stop_all_hosts()
+ # Creating configuration from JSON
+ clear_mroute(tgen)
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"]
+
+ step(
+ "configure PIM passive on receiver interface to verify no impact on IGMP join"
+ "and multicast traffic on pim passive interface"
+ )
+
+ raw_config = {
+ "c1": {"raw_config": ["interface {}".format(intf_c1_i4), "ip pim passive"]}
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("configure IGMPv2 and send IGMP joinon on PIM passive interface")
+ input_dict = {
+ "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]}
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send Mcast traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)")
+
+ input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]}
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict_starg = [
+ {
+ "dut": "c1",
+ "src_address": "*",
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "c1",
+ "src_address": source_i5,
+ "iif": topo["routers"]["c1"]["links"]["c2"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ step("(*,G) and (S,G) created on f1 and node verify using 'show ip mroute'")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"]
+ intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"]
+
+ step(
+ "configure PIM passive on upstream interface to verify"
+ "hello tx/rx counts are not incremented"
+ )
+
+ # Changing hello timer to 3sec for checking more number of packets
+
+ raw_config = {
+ "c1": {
+ "raw_config": [
+ "interface {}".format(intf_c1_c2),
+ "ip pim passive",
+ "ip pim hello 3",
+ ]
+ },
+ "c2": {"raw_config": ["interface {}".format(intf_c2_c1), "ip pim hello 3"]},
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify PIM hello tx/rx stats on C1")
+ state_dict = {
+ "c1": {
+ intf_c1_c2: ["helloTx", "helloRx"],
+ }
+ }
+
+ logger.info("waiting for 5 sec config to get apply and hello count update")
+ sleep(5)
+
+ c1_state_before = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ logger.info(
+ "sleeping for 30 sec hello interval timer to verify count are not increamented"
+ )
+ sleep(35)
+
+ c1_state_after = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify stats not increamented on c1")
+ result = verify_pim_stats_increament(c1_state_before, c1_state_after)
+ assert (
+ result is not True
+ ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result)
+
+ step("No impact observed on mroutes")
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("remove PIM passive and verify hello tx/rx is increamented")
+ raw_config = {
+ "c1": {
+ "raw_config": [
+ "interface {}".format(intf_c1_c2),
+ "no ip pim passive",
+ "ip pim hello 3",
+ ]
+ }
+ }
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ logger.info("waiting for 30 sec for pim hello to receive")
+ sleep(30)
+
+ c1_state_after = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("verify stats increamented on c1 after removing pim passive")
+ result = verify_pim_stats_increament(c1_state_before, c1_state_after)
+ assert result is True, "Testcase{} : Failed Error: {}" "stats incremented".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/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py
new file mode 100755
index 0000000..e755109
--- /dev/null
+++ b/tests/topotests/multicast_pim_sm_topo3/test_multicast_pim_sm_topo4.py
@@ -0,0 +1,1104 @@
+#!/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 multicast pim sm:
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+Following tests are covered:
+
+1. TC:48 Verify mroute after configuring black-hole route for RP and source
+2. TC:49 Verify mroute when RP is reachable using default route
+3. TC:50 Verify mroute when LHR,FHR,RP and transit routers reachable
+ using default routes
+4. TC:52 Verify PIM nbr after changing interface ip
+5. TC:53 Verify IGMP interface updated with correct detail after changing interface config
+6. TC:54 Verify received and transmit hello stats are getting cleared after PIM nbr reset
+
+
+"""
+
+import os
+import sys
+import time
+import pytest
+
+pytestmark = pytest.mark.pimd
+
+# 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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ apply_raw_config,
+ create_static_routes,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_mroutes,
+ clear_pim_interface_traffic,
+ verify_upstream_iif,
+ clear_mroute,
+ verify_pim_rp_info,
+ get_pim_interface_traffic,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from time import sleep
+
+
+TOPOLOGY = """
+
+
+ i4-----c1-------------c2---i5
+ | |
+ | |
+ i1-----l1------r2-----f1---i2
+ | | | |
+ | | | |
+ i7 i6 i3 i8
+
+ Description:
+ i1, i2, i3. i4, i5, i6, i7, i8 - FRR running iperf to send IGMP
+ join and traffic
+ l1 - LHR
+ f1 - FHR
+ r2 - FRR router
+ c1 - FRR router
+ c2 - FRR router
+"""
+
+# Global variables
+
+GROUP_RANGE = "224.0.0.0/4"
+IGMP_GROUP = "225.1.1.1/32"
+IGMP_JOIN = "225.1.1.1"
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+NEW_ADDRESS_1 = "192.168.20.1"
+NEW_ADDRESS_2 = "192.168.20.2"
+NEW_ADDRESS_1_SUBNET = "192.168.20.1/24"
+NEW_ADDRESS_2_SUBNET = "192.168.20.2/24"
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: {}".format(testsuite_run_time))
+ logger.info("=" * 40)
+ logger.info("Master Topology: \n {}".format(TOPOLOGY))
+
+ logger.info("Running setup_module to create topology")
+
+ json_file = "{}/multicast_pim_sm_topo4.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # 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 reset_stats(stats):
+ """
+ API to reset the stats
+
+ Parameters
+ ----------
+ * `stats` : State dictionary holding helloRx and helloTx values
+ """
+
+ for router, state_data in stats.items():
+ for state, value in state_data.items():
+ stats[router][state] = 0
+ logger.info(
+ "[DUT: %s]: stats %s value has reset" " reset, Current value: %s",
+ router,
+ state,
+ stats[router][state],
+ )
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_mroute_when_RP_reachable_default_route_p2(request):
+ """
+ TC_49 Verify mroute when and source RP is reachable using default route
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step(
+ "Remove c1-c2 connected link to simulate topo "
+ "c1(FHR)---l1(RP)----r2---f1-----c2(LHR)"
+ )
+
+ intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"]
+ intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"]
+ shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False)
+ shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_c2_i5 = topo["routers"]["c2"]["links"]["i5"]["interface"]
+ input_dict = {
+ "c2": {"igmp": {"interfaces": {intf_c2_i5: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "l1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from C1 to all the groups ( 225.1.1.1 to 225.1.1.5)")
+
+ input_src = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i4 = topo["routers"]["i4"]["links"]["c1"]["ipv4"].split("/")[0]
+
+ input_dict_starg = [
+ {
+ "dut": "c2",
+ "src_address": "*",
+ "iif": topo["routers"]["c2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["c2"]["links"]["i5"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "c2",
+ "src_address": source_i4,
+ "iif": topo["routers"]["c2"]["links"]["f1"]["interface"],
+ "oil": topo["routers"]["c2"]["links"]["i5"]["interface"],
+ }
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete static routes on c2")
+ input_dict = {
+ "c2": {
+ "static_routes": [
+ {
+ "network": ["1.0.4.11/32", "10.0.2.1/24", "10.0.1.2/24"],
+ "next_hop": "10.0.3.2",
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP info unknown after removing static route from c2 ")
+ dut = "c2"
+ rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0]
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify mroute not present after Delete of static routes on c1")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes(S,G) are present after delete of static routes on c1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream is present after delete of static routes on c1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "mroutes(*,G) are present after delete of static routes on c1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "upstream is present after delete of static routes on c1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("Configure default routes on c2")
+
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]}
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("applying ip nht config on c2")
+
+ raw_config = {"c2": {"raw_config": ["ip nht resolve-via-default"]}}
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify RP info is NOT unknown after removing static route from c2 ")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "RP info is unknown after removing static route from c2 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("Verify (s,g) populated after adding default route ")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify (*,g) populated after adding default route ")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroute_with_RP_default_route_all_nodes_p2(request):
+ """
+ TC_50 Verify mroute when LHR,FHR,RP and transit routers reachable
+ using default routes
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step(
+ "Remove c1-c2 connected link to simulate topo "
+ "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)"
+ )
+
+ intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"]
+ intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"]
+ shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False)
+ shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"]
+ input_dict = {
+ "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "l1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)")
+
+ input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict_starg = [
+ {
+ "dut": "c1",
+ "src_address": "*",
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "c1",
+ "src_address": source_i5,
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ step("Verify mroutes and iff upstream")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete static routes RP on all the nodes")
+ input_dict = {
+ "c2": {
+ "static_routes": [
+ {"network": ["1.0.4.11/32"], "next_hop": "10.0.3.2", "delete": True}
+ ]
+ },
+ "c1": {
+ "static_routes": [
+ {"network": ["1.0.4.11/32"], "next_hop": "10.0.2.2", "delete": True}
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {"network": ["1.0.4.11/32"], "next_hop": "10.0.12.1", "delete": True}
+ ]
+ },
+ "f1": {
+ "static_routes": [
+ {"network": ["1.0.4.11/32"], "next_hop": "10.0.7.2", "delete": True}
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("Verify RP info unknown after removing static route from c2 ")
+ dut = "c2"
+ rp_address = topo["routers"]["l1"]["links"]["lo"]["ipv4"].split("/")[0]
+ SOURCE = "Static"
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen,
+ data["dut"],
+ data["iif"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "upstream is still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure default routes on all the nodes")
+
+ intf_f1_c2 = topo["routers"]["f1"]["links"]["c2"]["ipv4"].split("/")[0]
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["ipv4"].split("/")[0]
+ intf_l1_r2 = topo["routers"]["l1"]["links"]["r2"]["ipv4"].split("/")[0]
+ intf_r2_f1 = topo["routers"]["r2"]["links"]["f1"]["ipv4"].split("/")[0]
+
+ input_dict = {
+ "c1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_c1}]},
+ "c2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_f1_c2}]},
+ "r2": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_l1_r2}]},
+ "f1": {"static_routes": [{"network": "0.0.0.0/0", "next_hop": intf_r2_f1}]},
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error {}".format(tc_name, result)
+
+ step("applying ip nht config on c2")
+
+ raw_config = {
+ "c1": {"raw_config": ["ip nht resolve-via-default"]},
+ "c2": {"raw_config": ["ip nht resolve-via-default"]},
+ "r2": {"raw_config": ["ip nht resolve-via-default"]},
+ "f1": {"raw_config": ["ip nht resolve-via-default"]},
+ "l1": {"raw_config": ["ip nht resolve-via-default"]},
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify RP info Not unknown after removing static route from c2 ")
+ dut = "c2"
+ step("Verify RP info is NOT unknown after removing static route from c2 ")
+ result = verify_pim_rp_info(
+ tgen, topo, dut, GROUP_RANGE_1, "Unknown", rp_address, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "RP info is unknown after removing static route from c2 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("Verify (s,g) populated after adding default route ")
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify (*,g) populated after adding default route ")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_PIM_hello_tx_rx_p1(request):
+ """
+ TC_54 Verify received and transmit hello stats
+ are getting cleared after PIM nbr reset
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ step(
+ "Remove c1-c2 connected link to simulate topo "
+ "c1(LHR)---l1(RP)----r2---f1-----c2(FHR)"
+ )
+
+ intf_c1_c2 = topo["routers"]["c1"]["links"]["c2"]["interface"]
+ intf_c2_c1 = topo["routers"]["c2"]["links"]["c1"]["interface"]
+ shutdown_bringup_interface(tgen, "c1", intf_c1_c2, False)
+ shutdown_bringup_interface(tgen, "c2", intf_c2_c1, False)
+
+ step("Enable the PIM on all the interfaces of FRR1, FRR2, FRR3")
+ step(
+ "Enable IGMP of FRR1 interface and send IGMP joins "
+ " from FRR1 node for group range (225.1.1.1-5)"
+ )
+
+ intf_c1_i4 = topo["routers"]["c1"]["links"]["i4"]["interface"]
+ input_dict = {
+ "c1": {"igmp": {"interfaces": {intf_c1_i4: {"igmp": {"version": "2"}}}}}
+ }
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_join = {"i4": topo["routers"]["i4"]["links"]["c1"]["interface"]}
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure static RP for (225.1.1.1-5) as R2")
+
+ input_dict = {
+ "l1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["l1"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send Mcast traffic from C2 to all the groups ( 225.1.1.1 to 225.1.1.5)")
+
+ input_src = {"i5": topo["routers"]["i5"]["links"]["c2"]["interface"]}
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ source_i5 = topo["routers"]["i5"]["links"]["c2"]["ipv4"].split("/")[0]
+
+ input_dict_starg = [
+ {
+ "dut": "c1",
+ "src_address": "*",
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ input_dict_sg = [
+ {
+ "dut": "c1",
+ "src_address": source_i5,
+ "iif": topo["routers"]["c1"]["links"]["l1"]["interface"],
+ "oil": topo["routers"]["c1"]["links"]["i4"]["interface"],
+ }
+ ]
+
+ step("(*,G) and (S,G) created on f1 and node verify using 'show ip mroute'")
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_l1_c1 = topo["routers"]["l1"]["links"]["c1"]["interface"]
+ intf_c1_l1 = topo["routers"]["c1"]["links"]["l1"]["interface"]
+
+ state_dict = {
+ "c1": {
+ intf_c1_l1: ["helloTx", "helloRx"],
+ }
+ }
+
+ c1_state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Flap PIM nbr while doing interface c1-l1 interface shut from f1 side")
+ shutdown_bringup_interface(tgen, "c1", intf_c1_l1, False)
+
+ """ Resetting the stats here since shutdown resets the stats.
+ """
+ reset_stats(c1_state_before)
+ shutdown_bringup_interface(tgen, "c1", intf_c1_l1, True)
+
+ step("verify stats after no shutdown on c1 and that they are incremented")
+
+ count = 0
+ done = False
+ while not done and count <= 7:
+ c1_state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(c1_state_before, c1_state_after)
+ if result is not True:
+ sleep(5)
+ count += 1
+ else:
+ done = True
+
+ assert (
+ result is True
+ ), "Testcase{} : Failed Error: {}" "stats is not incremented".format(
+ tc_name, result
+ )
+
+ step("verify before stats on l1")
+ l1_state_dict = {
+ "l1": {
+ intf_l1_c1: ["helloTx", "helloRx"],
+ }
+ }
+
+ l1_state_before = get_pim_interface_traffic(tgen, l1_state_dict)
+ assert isinstance(
+ l1_state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Flap PIM nbr while doing interface r2-c1 shut from r2 side")
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, False)
+
+ step(
+ "After shut the interface from r2 side , verify r2 side rx and tx of hello"
+ "counters are resetted show ip pim interface traffic"
+ )
+ shutdown_bringup_interface(tgen, "l1", intf_l1_c1, True)
+
+ step("verify stats after on l1 are incremented")
+ count = 0
+ done = False
+ while not done and count <= 7:
+ l1_state_after = get_pim_interface_traffic(tgen, l1_state_dict)
+ assert isinstance(
+ l1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(l1_state_before, l1_state_after)
+ if result is True:
+ sleep(5)
+ count += 1
+ else:
+ done = True
+
+ assert (
+ result is not True
+ ), "Testcase{} : Failed Error: {}" "stats incremented".format(tc_name, result)
+
+ step("Reinit the dict")
+ c1_state_before = {}
+ l1_state_before = {}
+ c1_state_after = {}
+ l1_state_after = {}
+
+ step("verify before stats on C1")
+ state_dict = {
+ "c1": {
+ intf_c1_l1: ["helloTx", "helloRx"],
+ }
+ }
+
+ c1_state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Flap c1-r2 pim nbr while changing ip address from c1 side")
+ c1_l1_ip_subnet = topo["routers"]["c1"]["links"]["l1"]["ipv4"]
+
+ raw_config = {
+ "c1": {
+ "raw_config": [
+ "interface {}".format(intf_c1_l1),
+ "no ip address {}".format(c1_l1_ip_subnet),
+ "ip address {}".format(NEW_ADDRESS_2_SUBNET),
+ ]
+ }
+ }
+
+ result = apply_raw_config(tgen, raw_config)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("verify stats after on c1 are incremented")
+ count = 0
+ done = False
+ while not done and count <= 7:
+ c1_state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ c1_state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(c1_state_before, c1_state_after)
+ if result is not True:
+ sleep(5)
+ count += 1
+ else:
+ done = True
+
+ assert result is True, "Testcase{} : Failed Error: {}" "stats incremented".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/multicast_pim_static_rp_topo1/__init__.py b/tests/topotests/multicast_pim_static_rp_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/__init__.py
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json
new file mode 100644
index 0000000..39c6840
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pim_static_rp.json
@@ -0,0 +1,119 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {"ipv4": "auto"}
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r0": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "r3": {"ipv4": "auto", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": {
+ "join-prune-interval": "5",
+ "keep-alive-timer": 15,
+ "register-suppress-time": 12
+ },
+ "igmp": {
+ "interfaces": {
+ "r1-r0-eth0": {
+ "igmp": {
+ "query": {"query-max-response-time": 40, "query-interval": 5},
+ "version": "2"
+ }
+ }
+ }
+ },
+ "static_routes": [
+ {"network": "10.0.4.0/24", "next_hop": "10.0.2.2"},
+ {"network": "10.0.5.0/24", "next_hop": "10.0.2.2"},
+ {"network": "10.0.6.0/24", "next_hop": "10.0.2.2", "admin_distance": 1},
+ {"network": "10.0.6.0/24", "next_hop": "10.0.1.2", "admin_distance": 2},
+ {"network": "1.0.2.17/32", "next_hop": "10.0.1.2", "admin_distance": 1},
+ {"network": "1.0.2.17/32", "next_hop": "10.0.2.2", "admin_distance": 2},
+ {"network": "1.0.3.17/32", "next_hop": "10.0.2.2"},
+ {"network": "1.0.4.17/32", "next_hop": "10.0.3.2"}
+ ]
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": {
+ "join-prune-interval": "5",
+ "keep-alive-timer": 15,
+ "register-suppress-time": 12,
+ "rp": [{"rp_addr": "1.0.2.17", "group_addr_range": ["224.0.0.0/4"]}]
+ },
+ "static_routes": [
+ {"network": "10.0.0.0/24", "next_hop": "10.0.1.1"},
+ {"network": "10.0.2.0/24", "next_hop": "10.0.1.1"},
+ {"network": "10.0.3.0/24", "next_hop": "10.0.1.1"},
+ {"network": "10.0.5.0/24", "next_hop": "10.0.4.2"},
+ {"network": "10.0.6.0/24", "next_hop": "10.0.4.2"},
+ {"network": "1.0.1.17/32", "next_hop": "10.0.1.1"},
+ {"network": "1.0.3.17/32", "next_hop": "10.0.4.2"},
+ {"network": "1.0.4.17/32", "next_hop": "10.0.1.1"}
+ ]
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r2": {"ipv4": "auto", "pim": "enable"},
+ "r4": {"ipv4": "auto", "pim": "enable"},
+ "r5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": {
+ "join-prune-interval": "5",
+ "keep-alive-timer": 15,
+ "register-suppress-time": 12
+ },
+ "static_routes": [
+ {"network": "10.0.0.0/24", "next_hop": "10.0.2.1"},
+ {"network": "10.0.1.0/24", "next_hop": "10.0.2.1"},
+ {"network": "10.0.3.0/24", "next_hop": "10.0.2.1"},
+ {"network": "1.0.1.17/32", "next_hop": "10.0.2.1"},
+ {"network": "1.0.2.17/32", "next_hop": "10.0.4.1"},
+ {"network": "1.0.4.17/32", "next_hop": "10.0.5.2"}
+ ]
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1": {"ipv4": "auto", "pim": "enable"},
+ "r3": {"ipv4": "auto", "pim": "enable"}
+ },
+ "pim": {
+ "join-prune-interval": "5",
+ "keep-alive-timer": 15,
+ "register-suppress-time": 12
+ },
+ "static_routes": [
+ {"network": "10.0.0.0/24", "next_hop": "10.0.3.1"},
+ {"network": "10.0.1.0/24", "next_hop": "10.0.3.1"},
+ {"network": "10.0.2.0/24", "next_hop": "10.0.3.1"},
+ {"network": "10.0.4.0/24", "next_hop": "10.0.5.1"},
+ {"network": "10.0.6.0/24", "next_hop": "10.0.5.1"},
+ {"network": "1.0.1.17/32", "next_hop": "10.0.3.1"},
+ {"network": "1.0.2.17/32", "next_hop": "10.0.3.1"},
+ {"network": "1.0.3.17/32", "next_hop": "10.0.5.1"}
+ ]
+ },
+ "r5": {
+ "links": {
+ "r3": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/multicast_pimv6_static_rp.json b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pimv6_static_rp.json
new file mode 100644
index 0000000..9edfae4
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/multicast_pimv6_static_rp.json
@@ -0,0 +1,197 @@
+{
+ "address_types": ["ipv6"],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {"ipv6": "auto"}
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r0": {"ipv6": "auto", "pim6": "enable"},
+ "r2": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r3": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r4": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }}
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r2": {},
+ "r3": {},
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ },
+ "mld": {
+ "interfaces": {
+ "r1-r0-eth0" :{
+ "mld":{
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r1": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r3": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }}
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r1": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r2": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r4": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r5": {"ipv6": "auto", "pim6": "enable"}
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r4": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv6": "auto", "type": "loopback", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r1": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }},
+ "r3": {"ipv6": "auto", "pim6": "enable",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }}
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r5": {
+ "links": {
+ "r3": {"ipv6": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
new file mode 100755
index 0000000..2a9fe32
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp.py
@@ -0,0 +1,1396 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 Multicast basic functionality:
+
+Topology:
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
+ adding and deleting the static RP
+TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
+TC_5: Verify OIF entry for RP is cleared when RP becomes unreachable
+TC_6: Verify IIF and OIL in "show ip pim state" updated properly when RP
+ becomes unreachable
+TC_7 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_8: Verify (*,G) prune is send towards the RP when RP becomes unreachable
+TC_9 : Verify RP configured after IGMP join received, PIM join towards RP is
+ sent immediately
+TC_10 : Verify RP becomes reachable after IGMP join received, PIM join
+ towards RP is sent immediately
+TC_11 : Verify PIM join send towards the higher preferred RP
+TC_12 : Verify PIM prune send towards the lower preferred RP
+TC_13 : Verify RPF interface is updated in mroute (kernel) when higher
+ preferred overlapping RP configured
+TC_14 : Verify IIF and OIL in "show ip pim state" updated properly when higher
+ preferred overlapping RP configured
+TC_15 : Verify upstream interfaces(IIF) and join state are updated when higher
+ preferred overlapping RP is configured
+TC_16 : Verify join is send to lower preferred RP, when higher preferred RP
+ gets deleted
+TC_17 : Verify prune is send to higher preferred RP when higher preferred RP
+ gets deleted
+TC_18 : Verify RPF interface updated in mroute when higher preferred RP gets
+ deleted
+TC_19 : Verify IIF and OIL in "show ip pim state" updated when higher
+ preferred overlapping RP is deleted
+TC_20 : Verify PIM upstream IIF updated when higher preferred overlapping RP
+ deleted
+TC_21_1 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_21_2 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_22_1 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_22_2 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_23 : Verify (*,G) and (S,G) populated correctly when RPT and SPT path are
+ different
+TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the
+ same path
+TC_25 : Verify (*,G) and (S,G) populated correctly after clearing the PIM ,
+ IGMP and mroutes joins
+TC_26 : Restart the PIMd process and verify PIM joins , and mroutes entries
+TC_27 : Configure multiple groups (10 grps) with same RP address
+TC_28 : Configure multiple groups (10 grps) with different RP address
+TC_29 : Verify IIF and OIL in updated in mroute when upstream interface
+ configure as RP
+TC_30 : Verify IIF and OIL change to other path after shut the primary path
+TC_31 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface.
+TC_32 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface
+"""
+
+import os
+import sys
+import time
+from time import sleep
+import datetime
+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, "../"))
+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.topogen import Topogen, get_topogen
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ verify_igmp_groups,
+ verify_upstream_iif,
+ verify_join_state_and_timer,
+ verify_mroutes,
+ verify_pim_neighbors,
+ get_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ clear_pim_interface_traffic,
+ clear_igmp_interfaces,
+ clear_pim_interfaces,
+ clear_mroute,
+ clear_mroute_verify,
+ McastTesterHelper,
+)
+
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
+# Global variables
+GROUP_RANGE_ALL = "224.0.0.0/4"
+GROUP_RANGE = "225.1.1.1/32"
+GROUP_RANGE_LIST_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+GROUP_RANGE_LIST_2 = [
+ "225.1.1.6/32",
+ "225.1.1.7/32",
+ "225.1.1.8/32",
+ "225.1.1.9/32",
+ "225.1.1.10/32",
+]
+GROUP_ADDRESS = "225.1.1.1"
+GROUP_ADDRESS_LIST_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_ADDRESS_LIST_2 = [
+ "225.1.1.6",
+ "225.1.1.7",
+ "225.1.1.8",
+ "225.1.1.9",
+ "225.1.1.10",
+]
+STAR = "*"
+SOURCE_ADDRESS = "10.0.6.2"
+SOURCE = "Static"
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Building topology from json file
+ build_topo_from_json(tgen, TOPO)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: %s", testsuite_run_time)
+ logger.info("=" * 40)
+
+ topology = """
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+ """
+ logger.info("Master Topology: \n %s", topology)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/multicast_pim_static_rp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global TOPO
+ TOPO = tgen.json_topo
+
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, TOPO)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, TOPO)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time())))
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def verify_mroute_repopulated(uptime_before, uptime_after):
+ """
+ API to compare uptime for mroutes
+
+ Parameters
+ ----------
+ * `uptime_before` : Uptime dictionary for any particular instance
+ * `uptime_after` : Uptime dictionary for any particular instance
+ """
+
+ for group in uptime_before.keys():
+ for source in uptime_before[group].keys():
+ if set(uptime_before[group]) != set(uptime_after[group]):
+ errormsg = (
+ "mroute (%s, %s) has not come"
+ " up after mroute clear [FAILED!!]" % (source, group)
+ )
+ return errormsg
+
+ d_1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S")
+ d_2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S")
+ if d_2 >= d_1:
+ errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % (
+ source,
+ group,
+ )
+ return errormsg
+
+ logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group)
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, _ in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_add_delete_static_RP_p0(request):
+ """
+ TC_1_P0 : Verify upstream interfaces(IIF) and join state are updated
+ properly after adding and deleting the static RP
+ TC_2_P0 : Verify IIF and OIL in "show ip pim state" updated properly
+ after adding and deleting the static RP
+ TC_3_P0: (*, G) Mroute entry are cleared when static RP gets deleted
+ TC_4_P0: Verify (*,G) prune is send towards the RP after deleting the
+ static RP
+
+ Topology used:
+ r0------r1-----r2
+ iperf DUT RP
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("pre-configuration to send IGMP join and multicast traffic")
+
+ step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1")
+ step("Configure r2 loopback interface as RP")
+ step("Enable PIM between r1 and r3")
+
+ step("r1: Verify show ip igmp group without any IGMP join")
+ dut = "r1"
+ interface = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, interface, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: igmp group present without any IGMP join \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r1: Verify show ip pim interface traffic without any IGMP join")
+ state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}}
+
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r0 : Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ rp_address = "1.0.2.17"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify ip pim join")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ step("r1: Delete RP configuration")
+
+ # Delete RP configuration
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: RP info present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream IIF interface present \n Error: {}".format(tc_name, result)
+ )
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream join state is up and join timer is running \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ # 20
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: mroutes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify show ip pim interface traffic without any IGMP join")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase {} : Failed \n state_before is not dictionary \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_SPT_RPT_path_same_p1(request):
+ """
+ TC_24_P1 : Verify (*,G) and (S,G) populated correctly when SPT and RPT
+ share the same path
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1 r3-----r5
+
+ r1 : LHR
+ r2 : RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ dut = "r1"
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r3-r4-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to R1")
+ step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send multicast traffic from R3")
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ iif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S, G) upstream join state is up and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r2-eth1"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_not_reachable_static_RP_p0(request):
+ """
+ TC_5_P0: Verify OIF entry for RP is cleared when RP becomes unreachable
+ TC_6_P0: Verify IIF and OIL in "show ip pim state" updated properly when
+ RP becomes unreachable
+ TC_7_P0 : Verify upstream interfaces(IIF) and join state are updated
+ properly after adding and deleting the static RP
+ TC_8_P0: Verify (*,G) prune is send towards the RP when RP becomes
+ unreachable
+
+ Topology used:
+ r0------r1-----r2
+ iperf DUT RP
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ dut = "r1"
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r1"
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "r1: (*,G) prune is not sent towards the RP interface, verify using"
+ "show ip pim interface traffic"
+ )
+ state_dict = {"r1": {"r1-r2-eth1": ["pruneTx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, state_before
+ )
+
+ step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1")
+ step("Configure r2 loopback interface as RP")
+ step("Enable PIM between r1 and r2")
+
+ step("r0 : Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify rp info")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ rp_address = "1.0.2.17"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 :Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Make RP un-reachable")
+ dut = "r1"
+ input_dict = {
+ dut: {
+ "static_routes": [
+ {"network": "1.0.2.17/32", "next_hop": "10.0.1.2", "delete": True}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Check RP detail using show ip pim rp-info OIF should be unknown")
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "r1 : OIL should be same and IIF should be cleared on R1 verify"
+ "using show ip pim state"
+ )
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "OIL is not same and IIF is not cleared on R1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r1: upstream IIF should be unknown , verify using show ip pim" "upstream")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream IIF is not unknown \n Error: {}".format(tc_name, result)
+ )
+
+ step(
+ "r1: join state should not be joined and join timer should stop,"
+ "verify using show ip pim upstream"
+ )
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: join state is joined and timer is not stopped \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step(
+ "r1: (*,G) prune is sent towards the RP interface, verify using"
+ "show ip pim interface traffic"
+ )
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("r1: (*, G) cleared from mroute table using show ip mroute")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: (*, G) are not cleared from mroute table \n Error: {}".format(
+ tc_name, result
+ )
+ )
+ logger.info("Expected behavior: %s", result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_add_RP_after_join_received_p1(request):
+ """
+ TC_9_P1 : Verify RP configured after IGMP join received, PIM join towards
+ RP is sent immediately
+
+ Topology used:
+ r0------r1-----r2
+ iperf DUT RP
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on R1 interface")
+ step("Configure r2 loopback interface as RP")
+ step("Enable PIM between r1 and r2")
+ step("Delete RP configuration from r1")
+
+ step("r1: Delete RP configuration")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify rp-info")
+ dut = "r1"
+ rp_address = "1.0.2.17"
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: rp-info is present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("joinTx value before join sent")
+ state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("r0 : Send IGMP join (225.1.1.1) to r1, when rp is not configured" "in r1")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: IGMP group is received on R1 verify using show ip igmp groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream IFF interface is present \n Error: {}".format(tc_name, result)
+ )
+
+ step("r1: Verify upstream join state and join timer")
+
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream join state is joined and timer is running \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: PIM state is up\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: mroutes are still present\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Configure static RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify rp-info")
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: %s", result)
+
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_reachable_static_RP_after_join_p0(request):
+ """
+ TC_10_P0 : Verify RP becomes reachable after IGMP join received, PIM join
+ towards RP is sent immediately
+
+ Topology used:
+ r0------r1-----r3
+ iperf DUT RP
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface and send IGMP " "join (225.1.1.1) to r1")
+ step("Configure r2 loopback interface as RP")
+ step("Enable PIM between r1 and r2")
+
+ step("r1 : Verify pim interface traffic")
+ state_dict = {"r1": {"r1-r2-eth1": ["joinTx"]}}
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, state_before
+ )
+
+ step("r1: Make RP un-reachable")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: Verify rp-info")
+ rp_address = "1.0.2.17"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_ADDRESS, "Unknown", rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Send IGMP join for 225.1.1.1")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream IIF interface")
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream IIF interface is present\n Error: {}".format(tc_name, result)
+ )
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, STAR, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: upstream join state is joined and timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r1 : Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: PIM state is up\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1 : Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: mroutes are still present\n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Make RP reachable")
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1 : Verify rp-info")
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: %s", result)
+
+ step("r1 : Verify pim interface traffic")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_send_join_on_higher_preffered_rp_p1(request):
+ """
+ TC_11_P1 : Verify PIM join send towards the higher preferred RP
+ TC_12_P1 : Verify PIM prune send towards the lower preferred RP
+ TC_13_P1 : Verify RPF interface is updated in mroute (kernel) when higher
+ preferred overlapping RP configured
+ TC_14_P1 : Verify IIF and OIL in "show ip pim state" updated properly when
+ higher preferred overlapping RP configured
+ TC_15_P1 : Verify upstream interfaces(IIF) and join state are updated when
+ higher preferred overlapping RP is configured
+ TC_16_P1 : Verify join is send to lower preferred RP, when higher
+ preferred RP gets deleted
+ TC_17_P1 : Verify prune is send to higher preferred RP when higher
+ preferred RP gets deleted
+ TC_18_P1 : Verify RPF interface updated in mroute when higher preferred RP
+ gets deleted
+ TC_19_P1 : Verify IIF and OIL in "show ip pim state" updated when higher
+ preferred overlapping RP is deleted
+ TC_20_P1 : Verify PIM upstream IIF updated when higher preferred
+ overlapping RP deleted
+
+ Topology used:
+ _______r2
+ |
+ iperf |
+ r0-----r1
+ |
+ |_______r4
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range " "224.0.0.0/4")
+ step("Configure RP on r4 (loopback interface) for the group range " "225.1.1.1/32")
+
+ step("r3 : Make all interface not reachable")
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r3-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ intf = "r3-r4-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ intf = "r2-r3-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r4"
+ intf = "r4-r3-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r1"
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1 : Verify joinTx count before sending join")
+ state_dict = {"r1": {"r1-r4-eth3": ["joinTx"], "r1-r2-eth1": ["pruneTx"]}}
+
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, state_before
+ )
+
+ step("r0 : Send IGMP join for 225.1.1.1")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static RP for group 225.1.1.1/32")
+ input_dict = {
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.4.17",
+ "group_addr_range": ["225.1.1.1/32"],
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify RP info for group 224.0.0.0/4")
+ rp_address_1 = "1.0.2.17"
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address_1, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify RP info for group 225.1.1.1")
+ rp_address_2 = "1.0.4.17"
+ iif = "r1-r4-eth3"
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_RANGE, iif, rp_address_2, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify join is sent to higher preferred RP")
+ step("r1 : Verify prune is sent to lower preferred RP")
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("r1 : Verify ip mroutes")
+ iif = "r1-r4-eth3"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify PIM state")
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("r1 : Verify joinTx, pruneTx count before RP gets deleted")
+ state_dict = {"r1": {"r1-r2-eth1": ["joinTx"], "r1-r4-eth3": ["pruneTx"]}}
+
+ state_before = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1 : Delete RP configuration for 225.1.1.1")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.4.17",
+ "group_addr_range": ["225.1.1.1/32"],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify rp-info for group 224.0.0.0/4")
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address_1, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1 : Verify rp-info for group 225.1.1.1")
+ iif = "r1-r4-eth3"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE, oif, rp_address_2, SOURCE, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: rp-info is present for group 225.1.1.1 \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step(
+ "r1 : Verify RPF interface updated in mroute when higher preferred"
+ "RP gets deleted"
+ )
+ iif = "r1-r2-eth1"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ logger.info("Expected behavior: %s", result)
+
+ step(
+ "r1 : Verify IIF and OIL in show ip pim state updated when higher"
+ "preferred overlapping RP is deleted"
+ )
+ result = verify_pim_state(tgen, dut, iif, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "r1 : Verify upstream IIF updated when higher preferred overlapping"
+ "RP deleted"
+ )
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "r1 : Verify upstream join state and join timer updated when higher"
+ "preferred overlapping RP deleted"
+ )
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "r1 : Verify join is sent to lower preferred RP, when higher"
+ "preferred RP gets deleted"
+ )
+ step(
+ "r1 : Verify prune is sent to higher preferred RP when higher"
+ " preferred RP gets deleted"
+ )
+ state_after = get_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ ARGS = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(ARGS))
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py
new file mode 100755
index 0000000..35303c3
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp1.py
@@ -0,0 +1,1424 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 Multicast basic functionality:
+
+Topology:
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
+ adding and deleting the static RP
+TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
+TC_5: Verify OIF entry for RP is cleared when RP becomes unreachable
+TC_6: Verify IIF and OIL in "show ip pim state" updated properly when RP
+ becomes unreachable
+TC_7 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_8: Verify (*,G) prune is send towards the RP when RP becomes unreachable
+TC_9 : Verify RP configured after IGMP join received, PIM join towards RP is
+ sent immediately
+TC_10 : Verify RP becomes reachable after IGMP join received, PIM join
+ towards RP is sent immediately
+TC_11 : Verify PIM join send towards the higher preferred RP
+TC_12 : Verify PIM prune send towards the lower preferred RP
+TC_13 : Verify RPF interface is updated in mroute (kernel) when higher
+ preferred overlapping RP configured
+TC_14 : Verify IIF and OIL in "show ip pim state" updated properly when higher
+ preferred overlapping RP configured
+TC_15 : Verify upstream interfaces(IIF) and join state are updated when higher
+ preferred overlapping RP is configured
+TC_16 : Verify join is send to lower preferred RP, when higher preferred RP
+ gets deleted
+TC_17 : Verify prune is send to higher preferred RP when higher preferred RP
+ gets deleted
+TC_18 : Verify RPF interface updated in mroute when higher preferred RP gets
+ deleted
+TC_19 : Verify IIF and OIL in "show ip pim state" updated when higher
+ preferred overlapping RP is deleted
+TC_20 : Verify PIM upstream IIF updated when higher preferred overlapping RP
+ deleted
+TC_21_1 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_21_2 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_22_1 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_22_2 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_23 : Verify (*,G) and (S,G) populated correctly when RPT and SPT path are
+ different
+TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the
+ same path
+TC_25 : Verify (*,G) and (S,G) populated correctly after clearing the PIM ,
+ IGMP and mroutes joins
+TC_26 : Restart the PIMd process and verify PIM joins , and mroutes entries
+TC_27 : Configure multiple groups (10 grps) with same RP address
+TC_28 : Configure multiple groups (10 grps) with different RP address
+TC_29 : Verify IIF and OIL in updated in mroute when upstream interface
+ configure as RP
+TC_30 : Verify IIF and OIL change to other path after shut the primary path
+TC_31 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface.
+TC_32 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface
+"""
+
+import os
+import sys
+import time
+from time import sleep
+import datetime
+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, "../"))
+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.topogen import Topogen, get_topogen
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ verify_igmp_groups,
+ verify_upstream_iif,
+ verify_join_state_and_timer,
+ verify_mroutes,
+ verify_pim_neighbors,
+ get_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ clear_pim_interface_traffic,
+ clear_igmp_interfaces,
+ clear_pim_interfaces,
+ clear_mroute,
+ clear_mroute_verify,
+ McastTesterHelper,
+)
+
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
+# Global variables
+GROUP_RANGE_ALL = "224.0.0.0/4"
+GROUP_RANGE = "225.1.1.1/32"
+GROUP_RANGE_LIST_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+GROUP_RANGE_LIST_2 = [
+ "225.1.1.6/32",
+ "225.1.1.7/32",
+ "225.1.1.8/32",
+ "225.1.1.9/32",
+ "225.1.1.10/32",
+]
+GROUP_ADDRESS = "225.1.1.1"
+GROUP_ADDRESS_LIST_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_ADDRESS_LIST_2 = [
+ "225.1.1.6",
+ "225.1.1.7",
+ "225.1.1.8",
+ "225.1.1.9",
+ "225.1.1.10",
+]
+STAR = "*"
+SOURCE_ADDRESS = "10.0.6.2"
+SOURCE = "Static"
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Building topology from json file
+ build_topo_from_json(tgen, TOPO)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: %s", testsuite_run_time)
+ logger.info("=" * 40)
+
+ topology = """
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+ """
+ logger.info("Master Topology: \n %s", topology)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/multicast_pim_static_rp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global TOPO
+ TOPO = tgen.json_topo
+
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, TOPO)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, TOPO)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time())))
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def verify_mroute_repopulated(uptime_before, uptime_after):
+ """
+ API to compare uptime for mroutes
+
+ Parameters
+ ----------
+ * `uptime_before` : Uptime dictionary for any particular instance
+ * `uptime_after` : Uptime dictionary for any particular instance
+ """
+
+ for group in uptime_before.keys():
+ for source in uptime_before[group].keys():
+ if set(uptime_before[group]) != set(uptime_after[group]):
+ errormsg = (
+ "mroute (%s, %s) has not come"
+ " up after mroute clear [FAILED!!]" % (source, group)
+ )
+ return errormsg
+
+ d_1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S")
+ d_2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S")
+ if d_2 >= d_1:
+ errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % (
+ source,
+ group,
+ )
+ return errormsg
+
+ logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group)
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, _ in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_RP_configured_as_LHR_1_p1(request):
+ """
+ TC_21_1_P1: Verify OIF and RPF for (*,G) and (S,G) when static RP configure
+ in LHR router
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1 : LHR/RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r1 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send the IGMP join from r0")
+ step("Send multicast traffic from r5")
+
+ step("r1 , r2, r3, r4: Delete existing RP configuration" "configure r1(LHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Configure r1(LHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ shutdown_bringup_interface(tgen, "r1", "lo", False)
+ sleep(5)
+ shutdown_bringup_interface(tgen, "r1", "lo", True)
+ sleep(5)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.1.17"
+ iif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S, G) upstream join state is joined and join"
+ " timer is running \n Error: {}".format(tc_name, result)
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_RP_configured_as_LHR_2_p1(request):
+ """
+ TC_21_2_P1: Verify OIF and RPF for (*,G) and (S,G) when static RP configure
+ in LHR router
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1 : LHR/RP
+ r3 : FHR
+
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r1 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send multicast traffic from r5")
+ step("Send the IGMP join from r0")
+
+ step("r1, r2, r3, r4: Delete existing RP configuration," "configure r1(LHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1, r2, r3, r4: Configure r1(LHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.1.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.1.17"
+ iif = "lo"
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_ADDRESS, iif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_RP_configured_as_FHR_1_p1(request):
+ """
+ TC_22_1_P1: Verify OIF and RFP for (*,G) and (S,G) when static RP configure
+ in FHR router
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1 : LHR
+ r3 : FHR/RP
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range" " 225.1.1.0/24")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send the IGMP join from r0")
+ step("Send multicast traffic from r5")
+
+ step("r1, r2, r3, r4: Delete existing RP configuration" "configure r3(FHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1, r2, r3, r4: Configure r3(FHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.3.17"
+ iif = "r1-r3-eth2"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_RP_configured_as_FHR_2_p2(request):
+ """
+ TC_22_2_P2: Verify OIF and RFP for (*,G) and (S,G) when static RP configure
+ in FHR router
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1 : LHR
+ r3 : FHR/RP
+ """
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range" " 225.1.1.0/24")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send multicast traffic from r5")
+ step("Send the IGMP join from r0")
+
+ step("r1, r2, r3, r4: Delete existing RP configuration" "configure r3(FHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1, r2, r3, r4: Configure r3(FHR) as RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.3.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.3.17"
+ iif = "r1-r3-eth2"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_SPT_RPT_path_different_p1(request):
+ """
+ TC_23_P1: Verify (*,G) and (S,G) populated correctly when RPT and SPT path
+ are different
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1: LHR
+ r2: RP
+ r3: FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to r1")
+ step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send multicast traffic from r3")
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ iif = "lo"
+ result = verify_pim_rp_info(tgen, TOPO, dut, GROUP_ADDRESS, iif, rp_address, SOURCE)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ dut = "r2"
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+def test_clear_pim_configuration_p1(request):
+ """
+ TC_25_P1: Verify (*,G) and (S,G) populated correctly after clearing the
+ PIM,IGMP and mroutes joins
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+ r1 : LHR
+ r2 : RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send the IGMP join from r0")
+ step("Send multicast traffic from r5")
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ oif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ iif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, iif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups timer restarted")
+ result = clear_igmp_interfaces(tgen, dut)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM neighbor timer restarted")
+ result = clear_pim_interfaces(tgen, dut)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM mroute timer restarted")
+ result = clear_mroute_verify(tgen, dut)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Uncomment next line for debugging
+ # tgen.mininet_cli()
+
+ write_test_footer(tc_name)
+
+
+
+
+if __name__ == "__main__":
+ ARGS = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(ARGS))
diff --git a/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py
new file mode 100755
index 0000000..991d7d5
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pim_static_rp2.py
@@ -0,0 +1,1799 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2019 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 Multicast basic functionality:
+
+Topology:
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
+ adding and deleting the static RP
+TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
+TC_5: Verify OIF entry for RP is cleared when RP becomes unreachable
+TC_6: Verify IIF and OIL in "show ip pim state" updated properly when RP
+ becomes unreachable
+TC_7 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_8: Verify (*,G) prune is send towards the RP when RP becomes unreachable
+TC_9 : Verify RP configured after IGMP join received, PIM join towards RP is
+ sent immediately
+TC_10 : Verify RP becomes reachable after IGMP join received, PIM join
+ towards RP is sent immediately
+TC_11 : Verify PIM join send towards the higher preferred RP
+TC_12 : Verify PIM prune send towards the lower preferred RP
+TC_13 : Verify RPF interface is updated in mroute (kernel) when higher
+ preferred overlapping RP configured
+TC_14 : Verify IIF and OIL in "show ip pim state" updated properly when higher
+ preferred overlapping RP configured
+TC_15 : Verify upstream interfaces(IIF) and join state are updated when higher
+ preferred overlapping RP is configured
+TC_16 : Verify join is send to lower preferred RP, when higher preferred RP
+ gets deleted
+TC_17 : Verify prune is send to higher preferred RP when higher preferred RP
+ gets deleted
+TC_18 : Verify RPF interface updated in mroute when higher preferred RP gets
+ deleted
+TC_19 : Verify IIF and OIL in "show ip pim state" updated when higher
+ preferred overlapping RP is deleted
+TC_20 : Verify PIM upstream IIF updated when higher preferred overlapping RP
+ deleted
+TC_21_1 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_21_2 : Verify OIF and RFP for (*,G) and (S,G) when static RP configure in
+ LHR router
+TC_22_1 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_22_2 : Verify OIF and RPF for (*,G) and (S,G) when static RP configure in
+ FHR router
+TC_23 : Verify (*,G) and (S,G) populated correctly when RPT and SPT path are
+ different
+TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the
+ same path
+TC_25 : Verify (*,G) and (S,G) populated correctly after clearing the PIM ,
+ IGMP and mroutes joins
+TC_26 : Restart the PIMd process and verify PIM joins , and mroutes entries
+TC_27 : Configure multiple groups (10 grps) with same RP address
+TC_28 : Configure multiple groups (10 grps) with different RP address
+TC_29 : Verify IIF and OIL in updated in mroute when upstream interface
+ configure as RP
+TC_30 : Verify IIF and OIL change to other path after shut the primary path
+TC_31 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface.
+TC_32 : Verify RP info and (*,G) mroute after deleting the RP and shut / no
+ shut the RPF interface
+"""
+
+import os
+import sys
+import time
+from time import sleep
+import datetime
+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, "../"))
+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.topogen import Topogen, get_topogen
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ topo_daemons,
+)
+from lib.pim import (
+ create_pim_config,
+ verify_igmp_groups,
+ verify_upstream_iif,
+ verify_join_state_and_timer,
+ verify_mroutes,
+ verify_pim_neighbors,
+ get_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ clear_pim_interface_traffic,
+ clear_igmp_interfaces,
+ clear_pim_interfaces,
+ clear_mroute,
+ clear_mroute_verify,
+ McastTesterHelper,
+)
+
+pytestmark = [pytest.mark.pimd, pytest.mark.staticd]
+
+
+# Global variables
+GROUP_RANGE_ALL = "224.0.0.0/4"
+GROUP_RANGE = "225.1.1.1/32"
+GROUP_RANGE_LIST_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+GROUP_RANGE_LIST_2 = [
+ "225.1.1.6/32",
+ "225.1.1.7/32",
+ "225.1.1.8/32",
+ "225.1.1.9/32",
+ "225.1.1.10/32",
+]
+GROUP_ADDRESS = "225.1.1.1"
+GROUP_ADDRESS_LIST_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_ADDRESS_LIST_2 = [
+ "225.1.1.6",
+ "225.1.1.7",
+ "225.1.1.8",
+ "225.1.1.9",
+ "225.1.1.10",
+]
+STAR = "*"
+SOURCE_ADDRESS = "10.0.6.2"
+SOURCE = "Static"
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Building topology from json file
+ build_topo_from_json(tgen, TOPO)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: %s", testsuite_run_time)
+ logger.info("=" * 40)
+
+ topology = """
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+ """
+ logger.info("Master Topology: \n %s", topology)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/multicast_pim_static_rp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global TOPO
+ TOPO = tgen.json_topo
+
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, TOPO)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, TOPO)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info("Testsuite end time: %s", time.asctime(time.localtime(time.time())))
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def verify_mroute_repopulated(uptime_before, uptime_after):
+ """
+ API to compare uptime for mroutes
+
+ Parameters
+ ----------
+ * `uptime_before` : Uptime dictionary for any particular instance
+ * `uptime_after` : Uptime dictionary for any particular instance
+ """
+
+ for group in uptime_before.keys():
+ for source in uptime_before[group].keys():
+ if set(uptime_before[group]) != set(uptime_after[group]):
+ errormsg = (
+ "mroute (%s, %s) has not come"
+ " up after mroute clear [FAILED!!]" % (source, group)
+ )
+ return errormsg
+
+ d_1 = datetime.datetime.strptime(uptime_before[group][source], "%H:%M:%S")
+ d_2 = datetime.datetime.strptime(uptime_after[group][source], "%H:%M:%S")
+ if d_2 >= d_1:
+ errormsg = "mroute (%s, %s) is not " "repopulated [FAILED!!]" % (
+ source,
+ group,
+ )
+ return errormsg
+
+ logger.info("mroute (%s, %s) is " "repopulated [PASSED!!]", source, group)
+
+ return True
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, _ in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+def test_restart_pimd_process_p2(request):
+ """
+ TC_26_P2: Restart the PIMd process and verify PIM upstream and mroutes
+ entries
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+ r1 : LHR
+ r2 : RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to R1")
+ step("Configure RP on r3 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("Enable the PIM on all the interfaces of r1, r2, r3 and r4 routers")
+ step("Send multicast traffic from R3")
+ step("Restart the PIMd process")
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ oif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", GROUP_ADDRESS, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ oil = "r1-r0-eth0"
+ logger.info("waiting for 10 sec to make sure old mroute time is higher")
+ sleep(10)
+ # Why do we then wait 60 seconds below before checking the routes?
+ uptime_before = verify_mroutes(
+ tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, mwait=60
+ )
+ assert isinstance(uptime_before, dict), "Testcase{} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("r1: Kill pimd process")
+ kill_router_daemons(tgen, "r1", ["pimd"])
+
+ step("r1 : Start pimd process")
+ start_router_daemons(tgen, "r1", ["pimd"])
+
+ logger.info("Waiting for 5sec to get PIMd restarted and mroute" " re-learned..")
+ sleep(5)
+
+ # Why do we then wait 10 seconds below before checking the routes?
+ uptime_after = verify_mroutes(
+ tgen, dut, STAR, GROUP_ADDRESS, iif, oil, return_uptime=True, mwait=10
+ )
+ assert isinstance(uptime_after, dict), "Testcase{} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_mroute_repopulated(uptime_before, uptime_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_multiple_groups_same_RP_address_p2(request):
+ """
+ TC_27_P2: Configure multiple groups (10 grps) with same RP address
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+
+ r1 : LHR
+ r2 : RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface and send IGMP join (225.1.1.1) to r1")
+ step("Configure RP on r2 (loopback interface) for the group range" "225.1.1.0/24")
+ step("Enable the PIM on all the interfaces of r1-r2-r3")
+ step("Send multicast traffic from r5 to all the groups")
+ step("r1 : Remove the groups to RP mapping one by one")
+ step("r1: Shut the upstream interfaces")
+ step("r1: No shut the upstream interfaces")
+ step("r1: Configure the RP again")
+ step("r1: Shut the receiver interfaces")
+ step("r1: No Shut the receiver interfaces")
+ step("r2: Verify RP info")
+
+ step("r2: verify rp-info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ oif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2
+ step("r0: Send IGMP join for 10 groups")
+ result = app_helper.run_join("r0", group_address_list, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", group_address_list, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ dut = "r2"
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Delete RP configuration")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No Shut the interface r1-r2-eth1 from R1 to R2")
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Configure RP")
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r0-eth0 from R1 to R2")
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No Shut the interface r1-r0-eth0 from R1 to R2")
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ dut = "r2"
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, group_address_list, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, group_address_list, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_multiple_groups_different_RP_address_p2(request):
+ """
+ TC_28_P2: Verify IIF and OIL in updated in mroute when upstream interface
+ configure as RP
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+ r1 : LHR
+ r2 & r4 : RP
+ r3 : FHR
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Delete existing RP configuration")
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.4.17",
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ }
+ ]
+ }
+ },
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify RP info")
+ dut = "r2"
+ rp_address = "1.0.2.17"
+ oif = "lo"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_LIST_1, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify RP info")
+ dut = "r4"
+ rp_address = "1.0.4.17"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_LIST_2, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ group_address_list = GROUP_ADDRESS_LIST_1 + GROUP_ADDRESS_LIST_2
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", group_address_list, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, group_address_list)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r5: Send multicast traffic for group 225.1.1.1")
+ result = app_helper.run_traffic("r5", group_address_list, "r3")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r4-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) upstream IIF interface")
+ dut = "r4"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) ip mroutes")
+ oif = "r4-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream IIF interface")
+ iif = "r4-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r4: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r4: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete RP configuration")
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.4.17",
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1, r2, r3, r4: Re-configure RP")
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_LIST_1,
+ }
+ ]
+ }
+ },
+ "r4": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.4.17",
+ "group_addr_range": GROUP_RANGE_LIST_2,
+ }
+ ]
+ }
+ },
+ }
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R4")
+ dut = "r1"
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No shut the interface r1-r2-eth1 from R1 to r4")
+ dut = "r1"
+ intf = "r1-r4-eth3"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Shut the interface r1-r0-eth0 from R1 to R0")
+ dut = "r1"
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No Shut the interface r1-r0-eth0 from R1 to R0")
+ dut = "r1"
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream IIF interface")
+ dut = "r2"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream IIF interface")
+ iif = "r2-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_1, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream IIF interface")
+ dut = "r1"
+ iif = "r1-r4-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream IIF interface")
+ iif = "r1-r3-eth2"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (S, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) upstream IIF interface")
+ dut = "r4"
+ iif = "lo"
+ result = verify_upstream_iif(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, iif, STAR, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (*, G) ip mroutes")
+ oif = "r4-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream IIF interface")
+ iif = "r4-r3-eth1"
+ result = verify_upstream_iif(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, joinState="NotJoined"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r4: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r4: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r4: Verify (S, G) ip mroutes")
+ oif = "none"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream IIF interface")
+ dut = "r3"
+ iif = "r3-r5-eth3"
+ result = verify_upstream_iif(tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (S, G) upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, iif, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (S,G) upstream state is joined and join timer is running\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (S, G) ip mroutes")
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, SOURCE_ADDRESS, GROUP_ADDRESS_LIST_2, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_shutdown_primary_path_p1(request):
+ """
+ TC_30_P1: Verify IIF and OIL change to other path after shut the primary
+ path
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | |
+ r0-----r1-------------r3
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ # Steps to execute
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("r1: Shut the link from r1 to r2")
+ step("r3: Shut the link from r1 to r3")
+ step("r1: No shut the link from r1 to r2")
+ step("r3: No shut the link from r1 to r3")
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.2.17"
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "Verify after shut the R1 to R2 link , verify join is reaching to RP"
+ "via other path"
+ )
+
+ logger.info("Waiting for 110 sec only if test run with crucible")
+
+ step("r1: Verify (*, G) ip mroutes")
+ dut = "r1"
+ iif = "r1-r3-eth2"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r3-eth1"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (*, G) ip mroutes")
+ dut = "r3"
+ iif = "r3-r2-eth1"
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Shut the link from R1 to R3 from R3 node")
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "Verify after shut of R1 to R3 link , verify (*,G) entries got"
+ " cleared from all the node R1, R2, R3"
+ )
+
+ step("r1: Verify (*, G) ip mroutes")
+ dut = "r1"
+ iif = "r1-r3-eth2"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (*, G) ip mroutes")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r3-eth1"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: Verify (*, G) ip mroutes")
+ dut = "r3"
+ iif = "r3-r2-eth1"
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r3: (*,G) mroutes are not cleared after shut of R1 to R3 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r3: No shutdown the link from R1 to R3 from R3 node")
+ dut = "r3"
+ intf = "r3-r1-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) ip mroutes")
+ dut = "r1"
+ iif = "r1-r3-eth2"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r3-eth1"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r3: Verify (*, G) ip mroutes")
+ dut = "r3"
+ iif = "r3-r2-eth1"
+ oif = "r3-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: No shutdown the link from R1 to R2 from R1 node")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) ip mroutes")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_delete_RP_shut_noshut_upstream_interface_p1(request):
+ """
+ TC_31_P1: Verify RP info and (*,G) mroute after deleting the RP and shut /
+ no shut the RPF interface.
+ Topology used:
+ ________r2_____
+ | |
+ iperf | |
+ r0-----r1-------------r3
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (loopback interface) for the group range" " 224.0.0.0/4")
+ step("r1: Delete the RP config")
+ step("r1: Shut and no shut the upstream interface (R1-R2) connected link")
+ step("r1: Shut and no shut the OIL interface")
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.2.17"
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ dut = "r1"
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes created")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes created")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Delete RP configuration")
+
+ # Delete RP configuration
+ input_dict = {
+ "r1": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Shut the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No shutdown the interface r1-r2-eth1 from R1 to R2")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Shutdown the OIL interface r1-r0-eth0 from R1 to R0 ")
+ dut = "r1"
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: No shutdown the OIL interface r1-r0-eth0 from R1 to R0")
+ dut = "r1"
+ intf = "r1-r0-eth0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("r1: Verify (*, G) ip mroutes cleared")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: (*,G) mroutes are not cleared after shut of R1 to R0 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (*, G) ip mroutes cleared")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (*,G) mroutes are not cleared after shut of R1 to R0 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_delete_RP_shut_noshut_RP_interface_p1(request):
+ """
+ TC_32_P1: Verify RP info and (*,G) mroute after deleting the RP and shut/
+ no shut the RPF interface
+
+ Topology used:
+ ________r2_____
+ | |
+ iperf | |
+ r0-----r1-------------r3
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Creating configuration from JSON")
+ reset_config_on_routers(tgen)
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ clear_pim_interface_traffic(tgen, TOPO)
+
+ step("Enable IGMP on r1 interface")
+ step("Configure RP on r2 (lo) for the group range" " 224.0.0.0/4")
+ step("r2: Delete the RP configuration")
+ step("r2: Shut the RP interface (lo)")
+ step("r1: Shut the interface(r1-r2-eth1, r1-r3-eth2) towards rp")
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ rp_address = "1.0.2.17"
+ iif = "r1-r2-eth1"
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_ALL, iif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r0: Send IGMP join")
+ result = app_helper.run_join("r0", GROUP_ADDRESS, "r1")
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify IGMP groups")
+ oif = "r1-r0-eth0"
+ result = verify_igmp_groups(tgen, dut, oif, GROUP_ADDRESS)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify (*, G) ip mroutes created")
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Verify (*, G) ip mroutes created")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Delete RP configuration")
+
+ # Delete RP configuration
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": "1.0.2.17",
+ "group_addr_range": GROUP_RANGE_ALL,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r2: Shut the RP interface lo")
+ dut = "r2"
+ intf = "lo"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: Shut the interface r1-r2-eth1 towards RP")
+ dut = "r1"
+ intf = "r1-r2-eth1"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: Shut the interface r1-r3-eth2 towards RP")
+ dut = "r1"
+ intf = "r1-r3-eth2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("r1: Verify (*, G) ip mroutes cleared")
+ dut = "r1"
+ iif = "r1-r2-eth1"
+ oif = "r1-r0-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: (*,G) mroutes are not cleared after shut of R1 to R2 and R3 link\n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ step("r2: Verify (*, G) ip mroutes cleared")
+ dut = "r2"
+ iif = "lo"
+ oif = "r2-r1-eth0"
+ result = verify_mroutes(tgen, dut, STAR, GROUP_ADDRESS, iif, oif, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r2: (*,G) mroutes are not cleared after shut of R1 to R2 and R3 link\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/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py
new file mode 100755
index 0000000..bd5473a
--- /dev/null
+++ b/tests/topotests/multicast_pim_static_rp_topo1/test_multicast_pimv6_static_rp.py
@@ -0,0 +1,414 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 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 Multicast basic functionality:
+
+Topology:
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+Test steps
+- Create topology (setup module)
+- Bring up topology
+
+TC_1 : Verify upstream interfaces(IIF) and join state are updated properly
+ after adding and deleting the static RP
+TC_2 : Verify IIF and OIL in "show ip pim state" updated properly after
+ adding and deleting the static RP
+TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+TC_4: Verify (*,G) prune is send towards the RP after deleting the static RP
+TC_24 : Verify (*,G) and (S,G) populated correctly when SPT and RPT share the
+ same path
+"""
+
+import os
+import sys
+import json
+import time
+import pytest
+from time import sleep
+import datetime
+
+# 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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ kill_router_daemons,
+ start_router_daemons,
+ create_static_routes,
+ check_router_status,
+ socat_send_igmp_join_traffic,
+ topo_daemons
+)
+from lib.pim import (
+ create_pim_config,
+ verify_igmp_groups,
+ verify_upstream_iif,
+ verify_join_state_and_timer,
+ verify_mroutes,
+ verify_pim_neighbors,
+ verify_pim_interface_traffic,
+ verify_pim_rp_info,
+ verify_pim_state,
+ clear_pim_interface_traffic,
+ clear_igmp_interfaces,
+ clear_pim_interfaces,
+ clear_mroute,
+ clear_mroute_verify,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+
+# Global variables
+GROUP_RANGE_V6 = "ff08::/64"
+IGMP_JOIN_V6 = "ff08::1"
+STAR = "*"
+SOURCE = "Static"
+
+pytestmark = [pytest.mark.pimd]
+
+
+def build_topo(tgen):
+ """Build function"""
+
+ # Building topology from json file
+ build_topo_from_json(tgen, TOPO)
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ testsuite_run_time = time.asctime(time.localtime(time.time()))
+ logger.info("Testsuite start time: %s", testsuite_run_time)
+ logger.info("=" * 40)
+
+ topology = """
+
+ _______r2_____
+ | |
+ iperf | | iperf
+ r0-----r1-------------r3-----r5
+ | |
+ |_____________|
+ r4
+
+ """
+ logger.info("Master Topology: \n %s", topology)
+
+ logger.info("Running setup_module to create topology")
+
+ # This function initiates the topology build with Topogen...
+ json_file = "{}/multicast_pimv6_static_rp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global TOPO
+ TOPO = tgen.json_topo
+
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, TOPO)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, TOPO)
+
+ # Verify PIM neighbors
+ result = verify_pim_neighbors(tgen, TOPO)
+ assert result is True, "setup_module :Failed \n Error:" " {}".format(result)
+
+ 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: %s", time.asctime(time.localtime(time.time())))
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] >= state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+#####################################################
+
+def test_pimv6_add_delete_static_RP_p0(request):
+ """
+ TC_1: Verify upstream interfaces(IIF) and join state are updated
+ properly after adding and deleting the static RP
+ TC_2: Verify IIF and OIL in "show ip pim state" updated properly
+ after adding and deleting the static RP
+ TC_3: (*, G) Mroute entry are cleared when static RP gets deleted
+ TC_4: Verify (*,G) prune is send towards the RP after deleting the
+ static RP
+
+ TOPOlogy used:
+ r0------r1-----r2
+ iperf DUT RP
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ step("Shut link b/w R1 and R3 and R1 and R4 as per tescase topology")
+ intf_r1_r3 = TOPO["routers"]["r1"]["links"]["r3"]["interface"]
+ intf_r1_r4 = TOPO["routers"]["r1"]["links"]["r4"]["interface"]
+ for intf in [intf_r1_r3, intf_r1_r4]:
+ shutdown_bringup_interface(tgen, "r1", intf, ifaceaction=False)
+
+ step("Enable PIM between r1 and r2")
+ step("Enable MLD on r1 interface and send IGMP " "join (FF08::1) to r1")
+ step("Configure r2 loopback interface as RP")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_V6,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify show ip pim interface traffic without any mld join")
+ state_dict = {
+ "r1": {TOPO["routers"]["r1"]["links"]["r2"]["interface"]: ["pruneTx"]}
+ }
+
+ state_before = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6")
+ assert isinstance(
+ state_before, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ step("send mld join (FF08::1) to R1")
+ intf = TOPO["routers"]["r0"]["links"]["r1"]["interface"]
+ intf_ip = TOPO["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0]
+ result = socat_send_igmp_join_traffic(
+ tgen, "r0", "UDP6-RECV", IGMP_JOIN_V6, intf, intf_ip, join=True
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ dut = "r1"
+ oif = TOPO["routers"]["r1"]["links"]["r2"]["interface"]
+ iif = TOPO["routers"]["r1"]["links"]["r0"]["interface"]
+ rp_address = TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split("/")[0]
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_V6, oif, rp_address, SOURCE
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, oif, STAR, IGMP_JOIN_V6)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(tgen, dut, oif, STAR, IGMP_JOIN_V6)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, oif, iif, IGMP_JOIN_V6)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, IGMP_JOIN_V6, oif, iif)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("r1: Delete RP configuration")
+ input_dict = {
+ "r2": {
+ "pim6": {
+ "rp": [
+ {
+ "rp_addr": TOPO["routers"]["r2"]["links"]["lo"]["ipv6"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_V6,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, TOPO, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("r1: Verify RP info")
+ result = verify_pim_rp_info(
+ tgen, TOPO, dut, GROUP_RANGE_V6, oif, rp_address, SOURCE, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} :Failed \n " "RP: {} info is still present \n Error: {}".format(
+ tc_name, rp_address, result
+ )
+
+ step("r1: Verify upstream IIF interface")
+ result = verify_upstream_iif(tgen, dut, oif, STAR, IGMP_JOIN_V6, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Upstream ({}, {}) is still in join state \n Error: {}".format(
+ tc_name, STAR, IGMP_JOIN_V6, result
+ )
+ )
+
+ step("r1: Verify upstream join state and join timer")
+ result = verify_join_state_and_timer(
+ tgen, dut, oif, STAR, IGMP_JOIN_V6, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "Upstream ({}, {}) timer is still running \n Error: {}".format(
+ tc_name, STAR, IGMP_JOIN_V6, result
+ )
+ )
+
+ step("r1: Verify PIM state")
+ result = verify_pim_state(tgen, dut, oif, iif, IGMP_JOIN_V6, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "PIM state for group: {} is still Active \n Error: {}".format(
+ tc_name, IGMP_JOIN_V6, result
+ )
+ )
+
+ step("r1: Verify ip mroutes")
+ result = verify_mroutes(tgen, dut, STAR, IGMP_JOIN_V6, oif, iif, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed \n "
+ "mroute ({}, {}) is still present \n Error: {}".format(
+ tc_name, STAR, IGMP_JOIN_V6, result
+ )
+ )
+
+ step("r1: Verify show ip pim interface traffic without any IGMP join")
+ state_after = verify_pim_interface_traffic(tgen, state_dict, addr_type="ipv6")
+ assert isinstance(
+ state_after, dict
+ ), "Testcase{} : Failed \n state_before is not dictionary \n " "Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed 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/multicast_pim_uplink_topo1/multicast_pim_uplink_topo1.json b/tests/topotests/multicast_pim_uplink_topo1/multicast_pim_uplink_topo1.json
new file mode 100644
index 0000000..fa98987
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo1/multicast_pim_uplink_topo1.json
@@ -0,0 +1,226 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {"ipv4": "10.0.0.0", "v4mask": 24, "link_local": "disable"},
+ "lo_prefix": {"ipv4": "1.0.", "v4mask": 32},
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2-link1": {"ipv4": "auto", "pim": "enable"},
+ "r2-link2": {"ipv4": "auto", "pim": "enable"},
+ "r2-link3": {"ipv4": "auto", "pim": "enable"},
+ "r2-link4": {"ipv4": "auto", "pim": "enable"},
+ "r3-link1": {"ipv4": "auto", "pim": "enable"},
+ "r3-link2": {"ipv4": "auto", "pim": "enable"},
+ "r3-link3": {"ipv4": "auto", "pim": "enable"},
+ "r3-link4": {"ipv4": "auto", "pim": "enable"},
+ "i1": {"ipv4": "auto", "pim": "enable"},
+ "i2": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link1": {},
+ "r1-link2": {},
+ "r1-link3": {},
+ "r1-link4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r1-link1": {},
+ "r1-link2": {},
+ "r1-link3": {},
+ "r1-link4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1-link1": {"ipv4": "auto", "pim": "enable"},
+ "r1-link2": {"ipv4": "auto", "pim": "enable"},
+ "r1-link3": {"ipv4": "auto", "pim": "enable"},
+ "r1-link4": {"ipv4": "auto", "pim": "enable"},
+ "r4-link1": {"ipv4": "auto", "pim": "enable"},
+ "r4-link2": {"ipv4": "auto", "pim": "enable"},
+ "r4-link3": {"ipv4": "auto", "pim": "enable"},
+ "r4-link4": {"ipv4": "auto", "pim": "enable"},
+ "i3": {"ipv4": "auto", "pim": "enable"},
+ "i4": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link1": {},
+ "r2-link2": {},
+ "r2-link3": {},
+ "r2-link4": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r2-link1": {},
+ "r2-link2": {},
+ "r2-link3": {},
+ "r2-link4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r1-link1": {"ipv4": "auto", "pim": "enable"},
+ "r1-link2": {"ipv4": "auto", "pim": "enable"},
+ "r1-link3": {"ipv4": "auto", "pim": "enable"},
+ "r1-link4": {"ipv4": "auto", "pim": "enable"},
+ "r4-link1": {"ipv4": "auto", "pim": "enable"},
+ "r4-link2": {"ipv4": "auto", "pim": "enable"},
+ "r4-link3": {"ipv4": "auto", "pim": "enable"},
+ "r4-link4": {"ipv4": "auto", "pim": "enable"},
+ "i5": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {}
+ }
+ },
+ "r4": {
+ "dest_link": {
+ "r3-link1": {},
+ "r3-link2": {},
+ "r3-link3": {},
+ "r3-link4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {"ipv4": "auto", "type": "loopback", "pim": "enable"},
+ "r2-link1": {"ipv4": "auto", "pim": "enable"},
+ "r2-link2": {"ipv4": "auto", "pim": "enable"},
+ "r2-link3": {"ipv4": "auto", "pim": "enable"},
+ "r2-link4": {"ipv4": "auto", "pim": "enable"},
+ "r3-link1": {"ipv4": "auto", "pim": "enable"},
+ "r3-link2": {"ipv4": "auto", "pim": "enable"},
+ "r3-link3": {"ipv4": "auto", "pim": "enable"},
+ "r3-link4": {"ipv4": "auto", "pim": "enable"},
+ "i6": {"ipv4": "auto", "pim": "enable"},
+ "i7": {"ipv4": "auto", "pim": "enable"}
+ },
+ "bgp": {
+ "local_as": "400",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static"},
+ {"redist_type": "connected"}
+ ],
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {}
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r4-link1": {},
+ "r4-link2": {},
+ "r4-link3": {},
+ "r4-link4": {}
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "i1": {
+ "links": {
+ "r1": {"ipv4": "auto"}
+ }
+ },
+ "i2": {
+ "links": {
+ "r1": {"ipv4": "auto"}
+ }
+ },
+ "i3": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i4": {
+ "links": {
+ "r2": {"ipv4": "auto"}
+ }
+ },
+ "i5": {
+ "links": {
+ "r3": {"ipv4": "auto"}
+ }
+ },
+ "i6": {
+ "links": {
+ "r4": {"ipv4": "auto"}
+ }
+ },
+ "i7": {
+ "links": {
+ "r4": {"ipv4": "auto"}
+ }
+ }
+ }
+}
diff --git a/tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py b/tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py
new file mode 100644
index 0000000..8a505a8
--- /dev/null
+++ b/tests/topotests/multicast_pim_uplink_topo1/test_multicast_pim_uplink_topo1.py
@@ -0,0 +1,3327 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 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 multicast pim uplink:
+
+1. Verify mroutes OIL and IIF updated correctly when receivers present inside
+ and outside of DUT
+2. Verify mroutes OIL and IIF updated correctly when source present inside
+ and outside of DUT
+3. Verify Mroutes and BSM forwarding when edge is transit node
+4. Verify mroutes updated correctly after source interface shut/no shut
+5. Verify mroutes updated correctly after receiver interface shut/no shut
+6. Verify mroute updated correctly after sending IGMP prune and join
+7. Verify mroute updated correctly after clear mroute
+8. Verify (*,G) mroute entries after changing the RP configuration
+9. Verify mroute entries after FRR service stop and start
+"""
+
+import os
+import sys
+import json
+import time
+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, "../"))
+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.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ step,
+ reset_config_on_routers,
+ shutdown_bringup_interface,
+ start_router,
+ stop_router,
+ create_static_routes,
+ required_linux_kernel_version,
+ topo_daemons,
+)
+from lib.bgp import create_router_bgp
+from lib.pim import (
+ create_pim_config,
+ create_igmp_config,
+ verify_igmp_groups,
+ verify_mroutes,
+ clear_pim_interface_traffic,
+ verify_upstream_iif,
+ clear_mroute,
+ verify_multicast_traffic,
+ verify_pim_rp_info,
+ verify_pim_interface_traffic,
+ verify_pim_state,
+ McastTesterHelper,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+# Global variables
+GROUP_RANGE_1 = [
+ "225.1.1.1/32",
+ "225.1.1.2/32",
+ "225.1.1.3/32",
+ "225.1.1.4/32",
+ "225.1.1.5/32",
+]
+IGMP_JOIN_RANGE_1 = ["225.1.1.1", "225.1.1.2", "225.1.1.3", "225.1.1.4", "225.1.1.5"]
+GROUP_RANGE_2 = [
+ "226.1.1.1/32",
+ "226.1.1.2/32",
+ "226.1.1.3/32",
+ "226.1.1.4/32",
+ "226.1.1.5/32",
+]
+IGMP_JOIN_RANGE_2 = ["226.1.1.1", "226.1.1.2", "226.1.1.3", "226.1.1.4", "226.1.1.5"]
+GROUP_RANGE_3 = [
+ "227.1.1.1/32",
+ "227.1.1.2/32",
+ "227.1.1.3/32",
+ "227.1.1.4/32",
+ "227.1.1.5/32",
+]
+IGMP_JOIN_RANGE_3 = ["227.1.1.1", "227.1.1.2", "227.1.1.3", "227.1.1.4", "227.1.1.5"]
+
+r1_r2_links = []
+r1_r3_links = []
+r2_r1_links = []
+r3_r1_links = []
+r2_r4_links = []
+r4_r2_links = []
+r4_r3_links = []
+HELLO_TIMER = 1
+HOLD_TIMER = 3
+
+pytestmark = [pytest.mark.pimd]
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.19")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ 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")
+
+ testdir = os.path.dirname(os.path.realpath(__file__))
+ json_file = "{}/multicast_pim_uplink_topo1.json".format(testdir)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, tgen.json_topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start deamons and then start routers
+ start_topology(tgen, daemons)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, tgen.json_topo)
+
+ # Pre-requisite data
+ get_interfaces_names(topo)
+
+ # XXX Replace this using "with McastTesterHelper()... " in each test if possible.
+ global app_helper
+ app_helper = McastTesterHelper(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()
+
+ app_helper.cleanup()
+
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#####################################################
+#
+# Local APIs
+#
+#####################################################
+
+
+def get_interfaces_names(topo):
+ """
+ API to fetch interfaces names and create list, which further would be used
+ for verification
+
+ Parameters
+ ----------
+ * `topo` : inout JSON data
+ """
+
+ for link in range(1, 5):
+
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(link)]["interface"]
+ r1_r2_links.append(intf)
+
+ intf = topo["routers"]["r1"]["links"]["r3-link{}".format(link)]["interface"]
+ r1_r3_links.append(intf)
+
+ intf = topo["routers"]["r2"]["links"]["r1-link{}".format(link)]["interface"]
+ r2_r1_links.append(intf)
+
+ intf = topo["routers"]["r3"]["links"]["r1-link{}".format(link)]["interface"]
+ r3_r1_links.append(intf)
+
+ intf = topo["routers"]["r2"]["links"]["r4-link{}".format(link)]["interface"]
+ r2_r4_links.append(intf)
+
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(link)]["interface"]
+ r4_r2_links.append(intf)
+
+ intf = topo["routers"]["r4"]["links"]["r3-link{}".format(link)]["interface"]
+ r4_r3_links.append(intf)
+
+
+def configure_static_routes_for_rp_reachability(tgen, topo):
+ """
+ API to configure static routes for rp reachability
+
+ Parameters
+ ----------
+ * `topo` : inout JSON data
+ """
+
+ for i in range(1, 5):
+ static_routes = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": [
+ topo["routers"]["r2"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i6"]["links"]["r4"]["ipv4"],
+ topo["routers"]["i7"]["links"]["r4"]["ipv4"],
+ topo["routers"]["r4"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r2"]["links"][
+ "r1-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ {
+ "network": [
+ topo["routers"]["r3"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i6"]["links"]["r4"]["ipv4"],
+ topo["routers"]["i7"]["links"]["r4"]["ipv4"],
+ topo["routers"]["r4"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r3"]["links"][
+ "r1-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ ]
+ },
+ "r2": {
+ "static_routes": [
+ {
+ "network": [
+ topo["routers"]["i6"]["links"]["r4"]["ipv4"],
+ topo["routers"]["i7"]["links"]["r4"]["ipv4"],
+ topo["routers"]["r4"]["links"]["lo"]["ipv4"],
+ topo["routers"]["r3"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r4"]["links"][
+ "r2-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ {
+ "network": [
+ topo["routers"]["r1"]["links"]["lo"]["ipv4"],
+ topo["routers"]["r3"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i1"]["links"]["r1"]["ipv4"],
+ topo["routers"]["i2"]["links"]["r1"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r1"]["links"][
+ "r2-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ ]
+ },
+ "r3": {
+ "static_routes": [
+ {
+ "network": [
+ topo["routers"]["r4"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i6"]["links"]["r4"]["ipv4"],
+ topo["routers"]["i7"]["links"]["r4"]["ipv4"],
+ topo["routers"]["r2"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r4"]["links"][
+ "r3-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ {
+ "network": [
+ topo["routers"]["r1"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i1"]["links"]["r1"]["ipv4"],
+ topo["routers"]["i2"]["links"]["r1"]["ipv4"],
+ topo["routers"]["r2"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r1"]["links"][
+ "r3-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ ]
+ },
+ "r4": {
+ "static_routes": [
+ {
+ "network": [
+ topo["routers"]["r3"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i1"]["links"]["r1"]["ipv4"],
+ topo["routers"]["i2"]["links"]["r1"]["ipv4"],
+ topo["routers"]["r1"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r3"]["links"][
+ "r4-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ {
+ "network": [
+ topo["routers"]["r2"]["links"]["lo"]["ipv4"],
+ topo["routers"]["i1"]["links"]["r1"]["ipv4"],
+ topo["routers"]["i2"]["links"]["r1"]["ipv4"],
+ topo["routers"]["r1"]["links"]["lo"]["ipv4"],
+ ],
+ "next_hop": topo["routers"]["r2"]["links"][
+ "r4-link{}".format(i)
+ ]["ipv4"].split("/")[0],
+ },
+ ]
+ },
+ }
+
+ result = create_static_routes(tgen, static_routes)
+ assert result is True, "API {} : Failed Error: {}".\
+ format(sys._getframe().f_code.co_name, result)
+
+
+def verify_state_incremented(state_before, state_after):
+ """
+ API to compare interface traffic state incrementing
+
+ Parameters
+ ----------
+ * `state_before` : State dictionary for any particular instance
+ * `state_after` : State dictionary for any particular instance
+ """
+
+ for router, state_data in state_before.items():
+ for state, value in state_data.items():
+ if state_before[router][state] > state_after[router][state]:
+ errormsg = (
+ "[DUT: %s]: state %s value has not"
+ " incremented, Initial value: %s, "
+ "Current value: %s [FAILED!!]"
+ % (
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+ )
+ return errormsg
+
+ logger.info(
+ "[DUT: %s]: State %s value is "
+ "incremented, Initial value: %s, Current value: %s"
+ " [PASSED!!]",
+ router,
+ state,
+ state_before[router][state],
+ state_after[router][state],
+ )
+
+ return True
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_mroutes_updated_with_correct_oil_iif_when_receiver_is_in_and_outside_DUT_p0(
+ request,
+):
+ """
+ Verify mroutes OIL and IIF updated correctly when receivers present inside
+ and outside of DUT
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["r4"]["interface"]}
+
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "IGMP groups are received on DUT and R4 verify using 'show ip igmp groups'"
+ " and 'show ip igmp groups json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, "r1", intf_r1_i1, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ result = verify_igmp_groups(tgen, "r4", intf_r4_i7, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Random shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "After shut of upstream interface from DUT verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random no shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut of upstream interface in alternate fashion from R4 side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, False)
+
+ step(
+ "After shut of upstream interface from R4 verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("No shut of upstream interface in alternate fashion from R4 side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Send different IGMP joins from DUT and R4 for group range (From DUT "
+ "225.1.1.1-5 and from R4 226.1.1.1-5)"
+ )
+
+ result = app_helper.run_join("i7", IGMP_JOIN_RANGE_2, "r4")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic for all the groups from R4")
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["r4"]["interface"]}
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_2, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "IGMP groups are received on DUT and R4 verify using 'show ip igmp groups'"
+ " and 'show ip igmp groups json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, "r1", intf_r1_i1, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ result = verify_igmp_groups(
+ tgen, "r4", intf_r4_i7, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_2
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] != "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Random shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "After shut of upstream interface from DUT verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random no shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_with_correct_oil_iif_when_source_is_in_and_outside_DUT_p0(
+ request,
+):
+ """
+ Verify mroutes OIL and IIF updated correctly when source present inside
+ and outside of DUT
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "IGMP groups are received on DUT and R4 verify using 'show ip igmp groups'"
+ " and 'show ip igmp groups json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, "r1", intf_r1_i1, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ result = verify_igmp_groups(tgen, "r4", intf_r4_i7, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Random shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "After shut of upstream interface from DUT verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random no shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random shut of upstream interface from R4 side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, False)
+
+ step(
+ "After shut of upstream interface from R4 verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random no shut of upstream interface from R4 side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Send different IGMP joins from DUT and R4 for group range (From DUT "
+ "225.1.1.1-5 and from R4 226.1.1.1-5)"
+ )
+
+ result = app_helper.run_join("i7", IGMP_JOIN_RANGE_2, "r4")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic for all the groups from R4")
+
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_2, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "IGMP groups are received on DUT and R4 verify using 'show ip igmp groups'"
+ " and 'show ip igmp groups json'"
+ )
+
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_igmp_groups(tgen, "r1", intf_r1_i1, IGMP_JOIN_RANGE_1)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ result = verify_igmp_groups(
+ tgen, "r4", intf_r4_i7, IGMP_JOIN_RANGE_1 + IGMP_JOIN_RANGE_2
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] != "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Random shut and no shut of upstream interface from DUT side")
+
+ step("Random shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "After shut of upstream interface from DUT verify mroutes has moved "
+ "to another interface (R2 or R3) and updated with correct OIL/IIF using"
+ " 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("Random no shut of upstream interface from DUT side")
+ for i in range(1, 5, 2):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "After no shut of upstream interface from DUT verify no change on"
+ "mroute using 'show ip mroute json'; 'show ip upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_verify_mroutes_forwarding_p0(request):
+ """
+ Verify Mroutes and BSM forwarding when edge is transit node
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("To make DUT as transit node , shut all the links from R3 to R4 nodes")
+ for i in range(1, 5):
+ intf = topo["routers"]["r3"]["links"]["r4-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r3", intf, False)
+
+ intf = topo["routers"]["r4"]["links"]["r3-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, False)
+
+ step("Enable IGMP on DUT and R3 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r3_i5 = topo["routers"]["r3"]["links"]["i5"]["interface"]
+ for dut, intf in zip(["r1", "r3"], [intf_r1_i1, intf_r3_i5]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 226.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i5": topo["routers"]["i5"]["links"]["r3"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_2, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 226.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_2,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 226.1.1.1-5")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_2, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "BSR and candidate RP info populated in R3 node verify using "
+ "'show ip pim rp-info json'"
+ )
+
+ rp_addr_r2 = topo["routers"]["r2"]["links"]["lo"]["ipv4"].split("/")[0]
+
+ result = verify_pim_rp_info(
+ tgen, topo, "r2", GROUP_RANGE_2, "lo", rp_addr_r2, "Static"
+ )
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ step(
+ "DUT created (*,G) and (S,G) entries as transit node for 226.1.1.1-5 "
+ "mroutes , OIL is local received and toward R3"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ },
+ {
+ "dut": "r3",
+ "src_address": "*",
+ "iif": r3_r1_links,
+ "oil": topo["routers"]["r3"]["links"]["i5"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source_i2,
+ "iif": r3_r1_links,
+ "oil": topo["routers"]["r3"]["links"]["i5"]["interface"],
+ },
+ {
+ "dut": "r3",
+ "src_address": source_i6,
+ "iif": r3_r1_links,
+ "oil": topo["routers"]["r3"]["links"]["i5"]["interface"],
+ },
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r3_i5 = topo["routers"]["r3"]["links"]["i5"]["interface"]
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r3": {"traffic_sent": [intf_r3_i5]},
+ "r1": {"traffic_sent": [intf_r1_i2]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Send different join from R3 (232.1.1.1-5) and traffic "
+ "from R4 for same range"
+ )
+
+ input_join = {"i5": topo["routers"]["i5"]["links"]["r3"]["interface"]}
+ result = app_helper.run_join("i5", IGMP_JOIN_RANGE_3, "r3")
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_3,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ input_src = {"i6": topo["routers"]["i6"]["links"]["r4"]["interface"]}
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_3, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("For different join (232.1.1.1-5) DUT created mroute OIL toward R3 only")
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {"dut": "r1", "src_address": "*", "iif": r1_r2_links, "oil": r1_r3_links},
+ {"dut": "r1", "src_address": source_i6, "iif": r1_r2_links, "oil": r1_r3_links},
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_3,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_3
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut from DUT to R2 and no shut from DUT")
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ step(
+ "After Shut (R1-R2) link from DUT, verify IIF on DUT changed to "
+ "different uplink interface on DUT 'show ip mroute json' for R4 so "
+ "connected urce"
+ )
+
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ }
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Traffic is received fine for R4 source 'show ip multicast json' on DUT")
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("No shut from DUT to R2 and no shut from DUT")
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and no shut DUT to R2 within 30 sec from DUT")
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "Shut and No shut in 30 sec time , verify on R2 added 2 entries in mroute "
+ ", shut link OIL got timeout after sometime"
+ )
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_2
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_correctly_after_source_interface_shut_noshut_p1(request):
+ """
+ Verify mroutes updated correctly after source interface shut/no shut
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("On R1 for local IGMP receivers, OIL towards RP is removed")
+
+ input_dict = [
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ }
+ ]
+
+ for data in input_dict:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed "
+ "Mroute IIF and OIF are same \n Error: {}".format(tc_name, result)
+ )
+
+ step("Shut and No shut source interface multiple time")
+
+ for i in range(0, 2):
+ step("Shut and no shut the source interface from DUT")
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_i2, False)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_i2, True)
+
+ step(
+ "After shut/no shut of source interface verify all the (S,G) "
+ "got re-learn and IIF/OIF pointing any of the links from R2 or "
+ "R3 verify using 'show ip mroute json'"
+ )
+
+ step(
+ "(S,G) OIL on R1 has only respective receiver port and uplink port "
+ " , RP side oil is removed"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("No change seen on (*,G) mroutes")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and no shut the source interface from R4")
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf_r4_i6, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_i6, True)
+
+ step(
+ "After shut/no shut of source interface verify all the (S,G) "
+ "got re-learn and IIF/OIF pointing any of the links from R2 or "
+ "R3 verify using 'show ip mroute json'"
+ )
+
+ step(
+ "(S,G) OIL on R1 has only respective receiver port and uplink port "
+ " , RP side oil is removed"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("No change seen on (*,G) mroutes")
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut source interface from R4 and no shut immediate after the "
+ "same source upstream expires from DUT"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf_r4_i6, False)
+ shutdown_bringup_interface(tgen, "r4", intf_r4_i6, True)
+
+ step(
+ "After no shut verify mroutes populated and multicast traffic resume ,"
+ " verify using 'show ip mroute json' 'show ip multicast count json'"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut source interface from DUT and no shut immediate after the "
+ "same source upstream expires from R4"
+ )
+
+ intf_r1_i2 = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf_r1_i2, False)
+ shutdown_bringup_interface(tgen, "r1", intf_r1_i2, True)
+
+ step(
+ "After no shut verify mroutes populated and multicast traffic resume ,"
+ " verify using 'show ip mroute json' 'show ip multicast count json'"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_correctly_after_receiver_interface_shut_noshut_p1(request):
+ """
+ Verify mroutes updated correctly after receiver interface shut/no shut
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif_r1_r2": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif_r1_r2": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif_r1_r2"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif_r1_r2"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": r1_r3_links + [topo["routers"]["r1"]["links"]["i1"]["interface"]],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut and no shut the source interface from DUT")
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ step(
+ "After shut/no shut of source interface verify all the (S,G) "
+ "got re-learn and IIF/OIF pointing any of the links from R2 or "
+ "R3 verify using 'show ip mroute json'"
+ )
+
+ step(
+ "(S,G) OIL on R1 has only respective receiver port and uplink port "
+ " , RP side oil is removed"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Shut the receiver interface from R4")
+ for i in range(1, 5):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, False)
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, True)
+
+ step(
+ "After shut/no shut of source interface verify all the (S,G) "
+ "got re-learn and IIF/OIF pointing any of the links from R2 or "
+ "R3 verify using 'show ip mroute json'"
+ )
+
+ step(
+ "(S,G) OIL on R1 has only respective receiver port and uplink port "
+ " , RP side oil is removed"
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut and no shut the receiver interface from DUT after PIM upstream" " timeout"
+ )
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, False)
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r1"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r1", intf, True)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Shut and no shut the receiver interface from R4 after PIM upstream " "timeout"
+ )
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, False)
+
+ for i in range(1, 5):
+ intf = topo["routers"]["r4"]["links"]["r2-link{}".format(i)]["interface"]
+ shutdown_bringup_interface(tgen, "r4", intf, True)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Traffic is received for all the groups , verify using "
+ "'show ip multicast count json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_after_sending_IGMP_prune_and_join_p1(request):
+ """
+ Verify mroute updated correctly after sending IGMP prune and join
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif_r1_r2": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif_r1_r2": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif_r1_r2"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif_r1_r2"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP prune and join for receivers connected on DUT")
+ step("Send IGMP prune and join for receivers connected on R4")
+
+ app_helper.stop_all_hosts()
+
+ step(
+ "After sending prune verify (*,G) and (S,G) entries got cleared "
+ "from all the nodes"
+ )
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif_r1_r2"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed "
+ " mroute are still present \n Error: {}".format(tc_name, result)
+ )
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed "
+ " mroute are still present \n Error: {}".format(tc_name, result)
+ )
+
+ step(
+ "After sending joins verify (*,G) and (S,G) entries got populated "
+ "again correct OIL and IIF info (any of the link of R2 or R3) verify "
+ "using 'show ip mroute json'"
+ )
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif_r1_r2"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Multicast traffic receiver for all the groups verify using "
+ "'show ip multicast count'"
+ )
+
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_after_after_clear_mroute_p1(request):
+ """
+ Verify mroute updated correctly after clear mroute
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ input_dict_starg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif_r1_r2": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif_r1_r2": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_starg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif_r1_r2"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif_r1_r2"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_sg = [
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Clear ip mroute from DUT")
+ clear_mroute(tgen, "r1")
+
+ step("Clear ip mroute from r4")
+ clear_mroute(tgen, "r4")
+
+ step(
+ "Multicast traffic receiver for all the groups verify using "
+ "'show ip multicast count'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_updated_after_changing_rp_config_p1(request):
+ """
+ Verify (*,G) mroute entries after changing the RP configuration
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Unconfigure BGP from all nodes as using static routes")
+
+ input_dict = {}
+ DUT = ["r1", "r2", "r3", "r4"]
+ ASN = [100, 200, 300, 400]
+ for dut, asn in zip(DUT, ASN):
+ temp = {dut: {"bgp": {}}}
+ input_dict.update(temp)
+
+ temp[dut]["bgp"].update({"local_as": asn, "delete": True})
+
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Configure static routes between nodes for making RP and source" "reachable")
+
+ configure_static_routes_for_rp_reachability(tgen, topo)
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_1, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": topo["routers"]["r1"]["links"]["i2"]["interface"],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Verify RP has (S,G) with none OIL or Upstream should be present using 'show ip mroute json'"
+ " 'show ip pim upstream json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {"dut": "r2", "src_address": source_i2, "iif": r2_r1_links, "oil": r2_r4_links},
+ {"dut": "r2", "src_address": source_i6, "iif": r2_r4_links, "oil": r2_r1_links},
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Verify pim interface traffic before changing RP")
+
+ intf_traffic = topo["routers"]["r4"]["links"]["r3-link1"]["interface"]
+ state_dict = {"r4": {intf_traffic: ["registerStopRx"]}}
+ state_before = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(state_before, dict), \
+ ("Testcase{} : Failed \n state_before is not dictionary \n "
+ "Error: {}".\
+ format(tc_name, result))
+
+ step("Change the RP to R3 loopback for same group range (225.1.1.1-5)")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ "delete": True,
+ }
+ ]
+ }
+ },
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1 + GROUP_RANGE_2,
+ }
+ ]
+ }
+ },
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After changing the RP to R3 , verify (S,G) with none OIL and "
+ "upstream got cleared from R2 and created on R3 verify using "
+ "'show ip mroute json'; 'show ip pim upstream json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] != "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_1
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step("(*,G) IIF on DUT is changed towards R3, verify using 'show ip mroute json'")
+
+ input_dict_star_g = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ }
+ ]
+
+ for data in input_dict_star_g:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "R4 is sending null register packets to R3 'show ip pim multicast traffic json'"
+ )
+ step("Verify pim interface traffic after changing RP")
+
+ state_after = verify_pim_interface_traffic(tgen, state_dict)
+ assert isinstance(state_before, dict), \
+ ("Testcase{} : Failed \n state_before is not dictionary \n "
+ "Error: {}".\
+ format(tc_name, result))
+
+ result = verify_state_incremented(state_before, state_after)
+ assert result is True, "Testcase{} : Failed Error: {}".format(tc_name, result)
+
+ step("Send new IGMP join for new group range (226.1.1.1-5)")
+
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_2, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from R4 to same group range")
+
+ input_src = {
+ "i6": topo["routers"]["i6"]["links"]["r4"]["interface"],
+ "i2": topo["routers"]["i2"]["links"]["r1"]["interface"],
+ }
+
+ for src, src_intf in input_src.items():
+ result = app_helper.run_traffic(src, IGMP_JOIN_RANGE_2, bind_intf=src_intf)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*.G) and (S,G) on LHR for group range (226.1.1.1-5)")
+ step(
+ "(*,G) joins sent towards new RP (R3) , mroute created verify using "
+ "'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_2,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_upstream_iif(
+ tgen, data["dut"], data["iif"], data["src_address"], IGMP_JOIN_RANGE_2
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "Traffic is received for groups (226.1.1.1-5) , (S,G) mroute updated "
+ "in DUT and R4 node verify using 'show ip multicast json'"
+ )
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Delete and Add the RP for group range 225.1.1.1-5 on DUT")
+
+ input_dict = {
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After delete of RP verify mroute got uninstall from DUT IIF updated as "
+ "unknown in PIM state using 'show ip mroute' 'show ip pim state json'"
+ )
+ step(
+ "No impact seen to on data path as RP config removed after SPT switchover "
+ "verify uptime and traffic using 'show ip mroute' 'show ip mroute count json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed "
+ "(*,G) entried are still present \n Error: {}".format(tc_name, result)
+ )
+
+ else:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ iif = topo["routers"]["r1"]["links"]["i2"]["interface"]
+ oil = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ result = verify_pim_state(tgen, "r1", iif, oil, IGMP_JOIN_RANGE_1, expected=False)
+ assert result is not True, (
+ "Testcase {} :Failed "
+ "PIM state is not unknown after deleting RP \n Error: {}".format(
+ tc_name, result
+ )
+ )
+
+ input_dict = {
+ "r3": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r3"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "After Adding the RP verify IIF updated again towards RP , and DUT"
+ " sending register packets towards RP, verify using 'show ip mroute'"
+ " and 'show ip pim int traffic'"
+ )
+ step(
+ "No impact seen to on data path as RP config removed after SPT "
+ "switchover verify uptime and traffic using 'show ip mroute' "
+ "'show ip mroute count json'"
+ )
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_mroutes_after_restart_frr_services_p2(request):
+ """
+ Verify mroute entries after FRR service stop and start
+ """
+
+ tgen = get_topogen()
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Creating configuration from JSON
+ app_helper.stop_all_hosts()
+ clear_mroute(tgen)
+ reset_config_on_routers(tgen)
+ clear_pim_interface_traffic(tgen, topo)
+
+ # Don"t run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Enable IGMP on DUT and R4 interface")
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ intf_r4_i7 = topo["routers"]["r4"]["links"]["i7"]["interface"]
+ for dut, intf in zip(["r1", "r4"], [intf_r1_i1, intf_r4_i7]):
+ input_dict = {dut: {"igmp": {"interfaces": {intf: {"igmp": {"version": "2"}}}}}}
+
+ result = create_igmp_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Send IGMP joins from DUT and R4 for group range 225.1.1.1-5")
+ input_join = {
+ "i1": topo["routers"]["i1"]["links"]["r1"]["interface"],
+ "i7": topo["routers"]["i7"]["links"]["r4"]["interface"],
+ }
+
+ for recvr, recvr_intf in input_join.items():
+ result = app_helper.run_join(recvr, IGMP_JOIN_RANGE_1, join_intf=recvr_intf)
+ assert result is True, "Testcase {}: Failed Error: {}".format(tc_name, result)
+
+ step("Configure RP as R2 for group range 225.1.1.1-5")
+
+ input_dict = {
+ "r2": {
+ "pim": {
+ "rp": [
+ {
+ "rp_addr": topo["routers"]["r2"]["links"]["lo"]["ipv4"].split(
+ "/"
+ )[0],
+ "group_addr_range": GROUP_RANGE_1,
+ }
+ ]
+ }
+ }
+ }
+
+ result = create_pim_config(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Done in base config: " "Configure EBGP peering between all the nodes")
+
+ step("Done in base config: " "Enable PIM on all the interfaces of all the nodes")
+
+ step("Send traffic from R4 for group range 225.1.1.1-5")
+
+ result = app_helper.run_traffic("i6", IGMP_JOIN_RANGE_1, "r4")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Send traffic from DUT for group range 225.1.1.1-5")
+
+ result = app_helper.run_traffic("i2", IGMP_JOIN_RANGE_1, "r1")
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ source_i6 = topo["routers"]["i6"]["links"]["r4"]["ipv4"].split("/")[0]
+ source_i2 = topo["routers"]["i2"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict_star_sg = [
+ {
+ "dut": "r1",
+ "src_address": "*",
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": "*",
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i6,
+ "iif": r1_r2_links + r1_r3_links,
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r1",
+ "src_address": source_i2,
+ "iif": r1_r2_links + [topo["routers"]["r1"]["links"]["i2"]["interface"]],
+ "oil": topo["routers"]["r1"]["links"]["i1"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i6,
+ "iif": topo["routers"]["r4"]["links"]["i6"]["interface"],
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ {
+ "dut": "r4",
+ "src_address": source_i2,
+ "iif": r4_r2_links + r4_r3_links,
+ "oil": topo["routers"]["r4"]["links"]["i7"]["interface"],
+ },
+ ]
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the FRR services using kill -9 pid(s) from DUT")
+ stop_router(tgen, "r1")
+
+ step("Start the FRR services from DUT")
+ start_router(tgen, "r1")
+
+ step("(*,G) IIF and OIL updated on both the nodes")
+
+ for data in input_dict_star_sg:
+ if data["src_address"] == "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "(S,G) IIF updated towards shortest path to source on both the nodes "
+ ", verify using 'show ip mroute' and 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] != "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ )
+ assert result is True, "Testcase {} : Failed Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "OIL is updated and traffic is received for all the groups on both "
+ "the nodes , verify using 'show ip multicast'; 'show ip multicast json'"
+ )
+
+ intf_r4_i6 = topo["routers"]["r4"]["links"]["i6"]["interface"]
+ intf_r1_i1 = topo["routers"]["r1"]["links"]["i1"]["interface"]
+ input_traffic = {
+ "r1": {"traffic_sent": [intf_r1_i1]},
+ "r4": {"traffic_received": [intf_r4_i6]},
+ }
+ result = verify_multicast_traffic(tgen, input_traffic)
+ assert result is True, "Testcase {} : Failed Error: {}".format(tc_name, result)
+
+ step("Stop the traffic and do frr services stop/start")
+ app_helper.stop_all_hosts()
+
+ stop_router(tgen, "r1")
+ start_router(tgen, "r1")
+
+ step(
+ "FRR services started with new PID , (S,G) not present "
+ "on DUT and R4 , verify using 'show ip mroute json'"
+ )
+
+ for data in input_dict_star_sg:
+ if data["src_address"] != "*":
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {}: Failed "
+ "mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+
+ step("Stop FRR on R4 node")
+
+ stop_router(tgen, "r4")
+
+ step(
+ "After stop of FRR on R4 node verify mroute on DUT should be "
+ "pimreg/prune state"
+ )
+ step("No OIL created toward R2 on R11 node")
+
+ for data in input_dict_star_sg:
+ result = verify_mroutes(
+ tgen,
+ data["dut"],
+ data["src_address"],
+ IGMP_JOIN_RANGE_1,
+ data["iif"],
+ data["oil"],
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed "
+ " Mroutes are still present \n Error: {}".format(tc_name, result)
+ )
+
+ step("Start FRR on R4 node")
+
+ start_router(tgen, "r4")
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/nhrp_topo/r1/nhrp4_cache.json b/tests/topotests/nhrp_topo/r1/nhrp4_cache.json
new file mode 100644
index 0000000..6426a93
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r1/nhrp4_cache.json
@@ -0,0 +1,29 @@
+{
+ "attr":{
+ "entriesCount":2
+ },
+ "table":[
+ {
+ "interface":"r1-gre0",
+ "type":"nhs",
+ "protocol":"10.255.255.2",
+ "nbma":"10.2.1.2",
+ "claimed_nbma":"10.2.1.2",
+ "used":false,
+ "timeout":true,
+ "auth":false,
+ "identity":""
+ },
+ {
+ "interface":"r1-gre0",
+ "type":"local",
+ "protocol":"10.255.255.1",
+ "nbma":"10.1.1.1",
+ "claimed_nbma":"10.1.1.1",
+ "used":false,
+ "timeout":false,
+ "auth":false,
+ "identity":"-"
+ }
+ ]
+}
diff --git a/tests/topotests/nhrp_topo/r1/nhrp_route4.json b/tests/topotests/nhrp_topo/r1/nhrp_route4.json
new file mode 100644
index 0000000..68b5a6e
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r1/nhrp_route4.json
@@ -0,0 +1,25 @@
+{
+ "10.255.255.2\/32":[
+ {
+ "prefix":"10.255.255.2\/32",
+ "protocol":"nhrp",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":10,
+ "metric":0,
+ "installed":true,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-gre0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/nhrp_topo/r1/nhrpd.conf b/tests/topotests/nhrp_topo/r1/nhrpd.conf
new file mode 100644
index 0000000..e5224e4
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r1/nhrpd.conf
@@ -0,0 +1,10 @@
+log stdout debugging
+! debug nhrp all
+interface r1-gre0
+ ip nhrp holdtime 500
+ ip nhrp shortcut
+ ip nhrp network-id 42
+ ip nhrp nhs dynamic nbma 10.2.1.2
+ ip nhrp registration no-unique
+ tunnel source r1-eth0
+exit
diff --git a/tests/topotests/nhrp_topo/r1/zebra.conf b/tests/topotests/nhrp_topo/r1/zebra.conf
new file mode 100644
index 0000000..b45670f
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r1/zebra.conf
@@ -0,0 +1,12 @@
+interface r1-eth0
+ ip address 10.1.1.1/24
+!
+ip route 10.2.1.0/24 10.1.1.3
+interface r1-gre0
+ ip address 10.255.255.1/32
+ no link-detect
+ ipv6 nd suppress-ra
+exit
+interface r1-eth1
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/nhrp_topo/r2/nhrp4_cache.json b/tests/topotests/nhrp_topo/r2/nhrp4_cache.json
new file mode 100644
index 0000000..34558e0
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r2/nhrp4_cache.json
@@ -0,0 +1,29 @@
+{
+ "attr":{
+ "entriesCount":2
+ },
+ "table":[
+ {
+ "interface":"r2-gre0",
+ "type":"local",
+ "protocol":"10.255.255.2",
+ "nbma":"10.2.1.2",
+ "claimed_nbma":"10.2.1.2",
+ "used":false,
+ "timeout":false,
+ "auth":false,
+ "identity":"-"
+ },
+ {
+ "interface":"r2-gre0",
+ "type":"dynamic",
+ "protocol":"10.255.255.1",
+ "nbma":"10.1.1.1",
+ "claimed_nbma":"10.1.1.1",
+ "used":false,
+ "timeout":true,
+ "auth":false,
+ "identity":""
+ }
+ ]
+}
diff --git a/tests/topotests/nhrp_topo/r2/nhrp_route4.json b/tests/topotests/nhrp_topo/r2/nhrp_route4.json
new file mode 100644
index 0000000..7393cba
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r2/nhrp_route4.json
@@ -0,0 +1,25 @@
+{
+ "10.255.255.1\/32":[
+ {
+ "prefix":"10.255.255.1\/32",
+ "protocol":"nhrp",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":10,
+ "metric":0,
+ "installed":true,
+ "internalNextHopNum":1,
+ "internalNextHopActiveNum":1,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-gre0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/nhrp_topo/r2/nhrpd.conf b/tests/topotests/nhrp_topo/r2/nhrpd.conf
new file mode 100644
index 0000000..f9185f9
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r2/nhrpd.conf
@@ -0,0 +1,10 @@
+! debug nhrp all
+log stdout debugging
+nhrp nflog-group 1
+interface r2-gre0
+ ip nhrp holdtime 500
+ ip nhrp redirect
+ ip nhrp network-id 42
+ ip nhrp registration no-unique
+ tunnel source r2-eth0
+exit
diff --git a/tests/topotests/nhrp_topo/r2/zebra.conf b/tests/topotests/nhrp_topo/r2/zebra.conf
new file mode 100644
index 0000000..9f40d4d
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r2/zebra.conf
@@ -0,0 +1,12 @@
+interface r2-eth0
+ ip address 10.2.1.2/24
+!
+ip route 10.1.1.0/24 10.2.1.3
+interface r2-gre0
+ ip address 10.255.255.2/32
+ no link-detect
+ ipv6 nd suppress-ra
+!
+interface r2-eth1
+ ip address 192.168.2.2/24
+!
diff --git a/tests/topotests/nhrp_topo/r3/zebra.conf b/tests/topotests/nhrp_topo/r3/zebra.conf
new file mode 100644
index 0000000..e77f955
--- /dev/null
+++ b/tests/topotests/nhrp_topo/r3/zebra.conf
@@ -0,0 +1,11 @@
+! debug zebra kernel
+! debug zebra rib
+! debug zebra events
+! debug zebra packet
+ip forwarding
+interface r3-eth0
+ ip address 10.1.1.3/24
+!
+interface r3-eth1
+ ip address 10.2.1.3/24
+exit
diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.dot b/tests/topotests/nhrp_topo/test_nhrp_topo.dot
new file mode 100644
index 0000000..6b68fb3
--- /dev/null
+++ b/tests/topotests/nhrp_topo/test_nhrp_topo.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-topo2";
+
+ # 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\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n10.0.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n2001:db8:4::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0"];
+ r2 -- sw1 [label="eth0"];
+
+ r2 -- sw2 [label="eth1"];
+ r3 -- sw2 [label="eth0"];
+
+ r2 -- sw3 [label="eth2"];
+ r4 -- sw3 [label="eth0"];
+}
diff --git a/tests/topotests/nhrp_topo/test_nhrp_topo.py b/tests/topotests/nhrp_topo/test_nhrp_topo.py
new file mode 100644
index 0000000..e6e5519
--- /dev/null
+++ b/tests/topotests/nhrp_topo/test_nhrp_topo.py
@@ -0,0 +1,228 @@
+#!/usr/bin/env python
+
+#
+# test_nhrp_topo.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2019 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_nhrp_topo.py: Test the FRR/Quagga NHRP daemon
+"""
+
+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
+from lib.common_config import required_linux_kernel_version
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.nhrpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 3 routers.
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+ 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["r2"])
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r1"])
+
+
+def _populate_iface():
+ tgen = get_topogen()
+ cmds_tot_hub = [
+ "ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.2.1.{1} remote 0.0.0.0",
+ "ip link set dev {0}-gre0 up",
+ "echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu",
+ "echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6",
+ "echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6",
+ ]
+
+ cmds_tot = [
+ "ip tunnel add {0}-gre0 mode gre ttl 64 key 42 dev {0}-eth0 local 10.1.1.{1} remote 0.0.0.0",
+ "ip link set dev {0}-gre0 up",
+ "echo 0 > /proc/sys/net/ipv4/ip_forward_use_pmtu",
+ "echo 1 > /proc/sys/net/ipv6/conf/{0}-eth0/disable_ipv6",
+ "echo 1 > /proc/sys/net/ipv6/conf/{0}-gre0/disable_ipv6",
+ ]
+
+ for cmd in cmds_tot_hub:
+ input = cmd.format("r2", "2")
+ logger.info("input: " + input)
+ output = tgen.net["r2"].cmd(input)
+ logger.info("output: " + output)
+
+ for cmd in cmds_tot:
+ input = cmd.format("r1", "1")
+ logger.info("input: " + input)
+ output = tgen.net["r1"].cmd(input)
+ logger.info("output: " + output)
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ result = required_linux_kernel_version("5.0")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ _populate_iface()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ )
+ if rname in ("r1", "r2"):
+ router.load_config(
+ TopoRouter.RD_NHRP, os.path.join(CWD, "{}/nhrpd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ logger.info("Launching NHRP")
+ for name in router_list:
+ router = tgen.gears[name]
+ router.start()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_protocols_convergence():
+ """
+ Assert that all protocols have converged before checking for the NHRP
+ statuses as they depend on it.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Check IPv4 routing tables.
+ logger.info("Checking NHRP cache and IPv4 routes for convergence")
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ if rname == "r3":
+ continue
+
+ json_file = "{}/{}/nhrp4_cache.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip nhrp cache json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+
+ output = router.vtysh_cmd("show ip nhrp cache")
+ logger.info(output)
+
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ for rname, router in router_list.items():
+ if rname == "r3":
+ continue
+
+ json_file = "{}/{}/nhrp_route4.json".format(CWD, router.name)
+ if not os.path.isfile(json_file):
+ logger.info("skipping file {}".format(json_file))
+ continue
+
+ expected = json.loads(open(json_file).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route nhrp json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=40, wait=0.5)
+
+ output = router.vtysh_cmd("show ip route nhrp")
+ logger.info(output)
+
+ assertmsg = '"{}" JSON output mismatches'.format(router.name)
+ assert result is None, assertmsg
+
+ for rname, router in router_list.items():
+ if rname == "r3":
+ continue
+ logger.info("Dump neighbor information on {}-gre0".format(rname))
+ output = router.run("ip neigh show")
+ logger.info(output)
+
+
+def test_nhrp_connection():
+ "Assert that the NHRP peers can find themselves."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ pingrouter = tgen.gears["r1"]
+ logger.info("Check Ping IPv4 from R1 to R2 = 10.255.255.2)")
+ output = pingrouter.run("ping 10.255.255.2 -f -c 1000")
+ logger.info(output)
+ if "1000 packets transmitted, 1000 received" not in output:
+ assertmsg = "expected ping IPv4 from R1 to R2 should be ok"
+ assert 0, assertmsg
+ else:
+ logger.info("Check Ping IPv4 from R1 to R2 OK")
+
+
+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/ospf6_ecmp_inter_area/r1/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf
new file mode 100644
index 0000000..6c7cb96
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r1/ospf6d.conf
@@ -0,0 +1,52 @@
+debug ospf6 lsa all
+debug ospf6 message all
+debug ospf6 route all
+debug ospf6 spf time
+debug ospf6 spf database
+debug ospf6 zebra send
+debug ospf6 zebra recv
+
+debug ospf6 lsa router
+debug ospf6 lsa router originate
+debug ospf6 lsa router examine
+debug ospf6 lsa router flooding
+debug ospf6 lsa as-external
+debug ospf6 lsa as-external originate
+debug ospf6 lsa as-external examine
+debug ospf6 lsa as-external flooding
+debug ospf6 lsa intra-prefix
+debug ospf6 lsa intra-prefix originate
+debug ospf6 lsa intra-prefix examine
+debug ospf6 lsa intra-prefix flooding
+debug ospf6 border-routers
+debug ospf6 zebra
+debug ospf6 interface
+debug ospf6 neighbor
+debug ospf6 flooding
+debug ospf6 gr helper
+debug ospf6 spf process
+debug ospf6 route intra-area
+debug ospf6 route inter-area
+debug ospf6 abr
+debug ospf6 asbr
+debug ospf6 nssa
+!
+interface r1-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r1-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r1-eth2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.1
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r1/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r1/zebra.conf
new file mode 100644
index 0000000..236ed0b
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r1/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf
new file mode 100644
index 0000000..49215f6
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r2/ospf6d.conf
@@ -0,0 +1,14 @@
+interface r2-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r2-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.2
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r2/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r2/zebra.conf
new file mode 100644
index 0000000..1b79b09
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r2/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:2::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf
new file mode 100644
index 0000000..8b918bf
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r3/ospf6d.conf
@@ -0,0 +1,14 @@
+interface r3-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r3-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.3
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r3/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r3/zebra.conf
new file mode 100644
index 0000000..efb3c35
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r3/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf
new file mode 100644
index 0000000..3cc8645
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r4/ospf6d.conf
@@ -0,0 +1,14 @@
+interface r4-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r4-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r4/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r4/zebra.conf
new file mode 100644
index 0000000..3479af4
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r4/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:4::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf
new file mode 100644
index 0000000..2a6c9ab
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r5/ospf6d.conf
@@ -0,0 +1,39 @@
+interface r5-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth1
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth2
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth3
+ ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth4
+ ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth5
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r5-eth6
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.5
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r5/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r5/zebra.conf
new file mode 100644
index 0000000..851cc9d
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r5/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:5::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf
new file mode 100644
index 0000000..a1f48b5
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r6/ospf6d.conf
@@ -0,0 +1,9 @@
+interface r6-eth0
+ ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.6
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r6/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r6/zebra.conf
new file mode 100644
index 0000000..b98da4b
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r6/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:6::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf
new file mode 100644
index 0000000..0e49b0d
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r7/ospf6d.conf
@@ -0,0 +1,11 @@
+interface lo
+ ipv6 ospf6 area 1
+!
+interface r7-eth0
+ ipv6 ospf6 area 1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.7
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf
new file mode 100644
index 0000000..a410be8
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r7/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:7::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf
new file mode 100644
index 0000000..fb5483c
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r8/ospf6d.conf
@@ -0,0 +1,9 @@
+interface r8-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.8
+ redistribute connected
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf
new file mode 100644
index 0000000..8e343d8
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r8/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:8::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf b/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf
new file mode 100644
index 0000000..57fa8e3
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r9/ospf6d.conf
@@ -0,0 +1,11 @@
+interface lo
+ ipv6 ospf6 area 0
+!
+interface r9-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.9
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf b/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf
new file mode 100644
index 0000000..e267496
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/r9/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface lo
+ ipv6 address 2001:db8:9::1/64
+!
diff --git a/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
new file mode 100644
index 0000000..553f936
--- /dev/null
+++ b/tests/topotests/ospf6_ecmp_inter_area/test_ospf6_ecmp_inter_area.py
@@ -0,0 +1,207 @@
+#!/usr/bin/env python
+
+# test_ospf6_ecmp_inter_area.py
+#
+# Copyright (c) 2021, 2022 by Martin Buck
+# Copyright (c) 2016 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.
+#
+
+r"""
+test_ospf6_ecmp_inter_area.py: Test OSPFv3 ECMP inter-area nexthop update
+
+Check proper removal of ECMP nexthops after a path used by one nexthop
+disappears. We remove a path by bringing down a link required by that
+path which is not adjacent to the router being checked. This is important
+because when bringing down adjacent links, the kernel might remove the
+nexthops itself without ospf6d having to do anything.
+
+Useful as a regression test for #9720.
+
+Topology:
+ .
+ Area 0 . Area 1
+ .
+ -- R2 -- . ---- R6
+ / \ ./
+R1 -- R3 -- R5 ---- R7 Area 1
+ \ / \\ ..............
+ -- R4 -- \--- R8 Area 0
+ \
+ -- R9
+
+We check routes on R1, primarily those towards R6/7/8/9. Those to R6/7 are
+inter-area routes with R5 being ABR, those to R8/9 are intra-area routes
+and are used for reference. R6/R8 announce external routes, R7/R9 announce
+internal routes.
+
+With all links up, we expect 3 ECMP paths and 3 nexthops on R1 towards each
+of R6/7/8/9. Then we bring down the R2-R5 link, causing only 2 remaining
+paths and 2 nexthops on R1. The test is successful if the number of nexthops
+for the routes on R1 is as expected.
+"""
+
+import os
+import sys
+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.
+
+pytestmark = [pytest.mark.ospf6d]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 9 routers
+ for routern in range(1, 10):
+ tgen.add_router("r{}".format(routern))
+
+ tgen.gears["r1"].add_link(tgen.gears["r2"])
+ tgen.gears["r1"].add_link(tgen.gears["r3"])
+ tgen.gears["r1"].add_link(tgen.gears["r4"])
+ tgen.gears["r2"].add_link(tgen.gears["r5"])
+ tgen.gears["r3"].add_link(tgen.gears["r5"])
+ tgen.gears["r4"].add_link(tgen.gears["r5"])
+ tgen.gears["r5"].add_link(tgen.gears["r6"])
+ tgen.gears["r5"].add_link(tgen.gears["r7"])
+ tgen.gears["r5"].add_link(tgen.gears["r8"])
+ tgen.gears["r5"].add_link(tgen.gears["r9"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF6, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def test_wait_protocol_convergence():
+ "Wait for OSPFv3 to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_neighbor_full(router, neighbor):
+ "Wait until OSPFv3 neighborship is full"
+ logger.info("waiting for OSPFv3 router '{}' neighborship with '{}'".format(router, neighbor))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 neighbor json",
+ {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None,
+ count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_neighbor_full("r1", "10.254.254.2")
+ expect_neighbor_full("r1", "10.254.254.3")
+ expect_neighbor_full("r1", "10.254.254.4")
+ expect_neighbor_full("r2", "10.254.254.1")
+ expect_neighbor_full("r2", "10.254.254.5")
+ expect_neighbor_full("r3", "10.254.254.1")
+ expect_neighbor_full("r3", "10.254.254.5")
+ expect_neighbor_full("r4", "10.254.254.1")
+ expect_neighbor_full("r4", "10.254.254.5")
+ expect_neighbor_full("r5", "10.254.254.2")
+ expect_neighbor_full("r5", "10.254.254.3")
+ expect_neighbor_full("r5", "10.254.254.4")
+ expect_neighbor_full("r5", "10.254.254.6")
+ expect_neighbor_full("r5", "10.254.254.7")
+ expect_neighbor_full("r5", "10.254.254.8")
+ expect_neighbor_full("r5", "10.254.254.9")
+ expect_neighbor_full("r6", "10.254.254.5")
+ expect_neighbor_full("r7", "10.254.254.5")
+ expect_neighbor_full("r8", "10.254.254.5")
+ expect_neighbor_full("r9", "10.254.254.5")
+
+def test_ecmp_inter_area():
+ "Test whether OSPFv3 ECMP nexthops are properly updated for inter-area routes after link down"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def num_nexthops(router):
+ routes = tgen.gears[router].vtysh_cmd("show ipv6 ospf6 route json", isjson=True)
+ route_prefixes_infos = sorted(routes.get("routes", {}).items())
+ return [len(ri.get("nextHops", [])) for rp, ri in route_prefixes_infos]
+
+ def expect_num_nexthops(router, expected_num_nexthops, count):
+ "Wait until number of nexthops for routes matches expectation"
+ logger.info("waiting for OSPFv3 router '{}' nexthops {}".format(router, expected_num_nexthops))
+ test_func = partial(num_nexthops, router)
+ _, result = topotest.run_and_expect(test_func, expected_num_nexthops,
+ count=count, wait=3)
+ assert result == expected_num_nexthops, \
+ "'{}' wrong number of route nexthops".format(router)
+
+ # Check nexthops pre link-down
+ expect_num_nexthops("r1", [1, 1, 1, 3, 3, 3, 3, 3], 4)
+
+ logger.info("triggering R2-R4 link down")
+ tgen.gears["r2"].run("ip link set r2-eth1 down")
+
+ #tgen.mininet_cli()
+ # Check nexthops post link-down
+ expect_num_nexthops("r1", [1, 1, 1, 2, 2, 2, 2, 2], 8)
+
+
+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/ospf6_gr_topo1/__init__.py b/tests/topotests/ospf6_gr_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/__init__.py
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf
new file mode 100644
index 0000000..8a9b4eb
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/ospf6d.conf
@@ -0,0 +1,30 @@
+:assword 1
+hostname rt1
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 1
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 1.1.1.1
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..58fc114
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_database.json
@@ -0,0 +1,95 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"1",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..cb88358
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..0c69310
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json
new file mode 100644
index 0000000..66ee57c
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..01ecd64
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt1/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2001:db8:1000::1/128
+!
+interface stub1
+!
+interface eth-rt2
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf
new file mode 100644
index 0000000..bcce341
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt2
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt1
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 1
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 2.2.2.2
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..fb16326
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_database.json
@@ -0,0 +1,183 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"1",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"1",
+ "interface":"eth-rt1",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"1.1.1.1"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..e4f27bf
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"1.1.1.1",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt1",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..34013a1
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json
new file mode 100644
index 0000000..624ff70
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..5e5731b
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt2/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+ ipv6 address 2001:db8:1000::2/128
+!
+interface eth-rt1
+!
+interface eth-rt3
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf
new file mode 100644
index 0000000..8dba58e
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/ospf6d.conf
@@ -0,0 +1,41 @@
+password 1
+hostname rt3
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt2
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 3.3.3.3
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..f8a8f76
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_database.json
@@ -0,0 +1,144 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt2",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..d0d7f45
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,28 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"2.2.2.2",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt2",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..ee516b9
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json
new file mode 100644
index 0000000..f9b43dc
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..ca3eea4
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt3/zebra.conf
@@ -0,0 +1,24 @@
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+ ipv6 address 2001:db8:1000::3/128
+!
+interface eth-rt2
+!
+interface eth-rt4
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf
new file mode 100644
index 0000000..9be5321
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt4
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt5
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 4.4.4.4
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..0954d1b
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_database.json
@@ -0,0 +1,188 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"eth-rt5",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..36abba4
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"5.5.5.5",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt5",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..3e5f17f
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json
new file mode 100644
index 0000000..f5212da
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..c488b2a
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt4/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+ ipv6 address 2001:db8:1000::4/128
+!
+interface eth-rt3
+!
+interface eth-rt5
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf
new file mode 100644
index 0000000..98ba8bc
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/ospf6d.conf
@@ -0,0 +1,29 @@
+password 1
+hostname rt5
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 2
+ ipv6 ospf network point-to-point
+!
+interface eth-rt4
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 2
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 5.5.5.5
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..4a163b9
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_database.json
@@ -0,0 +1,100 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"5.5.5.5"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"4.4.4.4",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"5.5.5.5",
+ "payload":"2001:db8:1000::5\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"2",
+ "interface":"eth-rt4",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"5.5.5.5"
+ }
+ ]
+ },
+ {
+ "areaId":"2",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..9b6ac91
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"4.4.4.4",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt4",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..a56c326
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json
new file mode 100644
index 0000000..5ea4f69
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..43ed901
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt5/zebra.conf
@@ -0,0 +1,20 @@
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+ ipv6 address 2001:db8:1000::5/128
+!
+interface eth-rt4
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf
new file mode 100644
index 0000000..d6d6f0c
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/ospf6d.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt6
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 0
+ ipv6 ospf network point-to-point
+!
+interface eth-rt3
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 0
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+interface eth-rt7
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 6.6.6.6
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..71872d1
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_database.json
@@ -0,0 +1,183 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"2.2.2.2"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"4.4.4.4"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::7\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"2.2.2.2",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"7.7.7.7"
+ },
+ {
+ "type":"INP",
+ "advRouter":"2.2.2.2",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"3.3.3.3",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"4.4.4.4",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"INP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"0",
+ "interface":"eth-rt3",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"3.3.3.3"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ }
+ ]
+ },
+ {
+ "areaId":"0",
+ "interface":"lo",
+ "lsa":[
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"eth-rt7",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..aba181b
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,20 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"3.3.3.3",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt3",
+ "interfaceState":"PointToPoint"
+ },
+ {
+ "neighborId":"7.7.7.7",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt7",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..c9494a9
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json
new file mode 100644
index 0000000..862f1ba
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt3",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt7",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..5ec27de
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt6/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+ ipv6 address 2001:db8:1000::6/128
+!
+interface eth-rt3
+!
+interface eth-rt7
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf
new file mode 100644
index 0000000..cee6ee5
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/ospf6d.conf
@@ -0,0 +1,30 @@
+password 1
+hostname rt7
+log file ospf6d.log
+log commands
+!
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router flooding
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 graceful-restart
+! debug ospf6 spf process
+!
+interface lo
+ ipv6 ospf area 3
+ ipv6 ospf network point-to-point
+!
+interface eth-rt6
+ ipv6 ospf network point-to-point
+ ipv6 ospf area 3
+ ipv6 ospf hello-interval 3
+ ipv6 ospf dead-interval 9
+!
+router ospf6
+ ospf6 router-id 7.7.7.7
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json
new file mode 100644
index 0000000..e70eb57
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_database.json
@@ -0,0 +1,95 @@
+{
+ "areaScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "lsa":[
+ {
+ "type":"Rtr",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Rtr",
+ "advRouter":"7.7.7.7"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::6\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::2\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::3\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::4\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"IAP",
+ "advRouter":"6.6.6.6",
+ "payload":"2001:db8:1000::5\/128"
+ },
+ {
+ "type":"IAR",
+ "advRouter":"6.6.6.6",
+ "payload":"1.1.1.1"
+ },
+ {
+ "type":"INP",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ],
+ "interfaceScopedLinkStateDb":[
+ {
+ "areaId":"3",
+ "interface":"eth-rt6",
+ "lsa":[
+ {
+ "type":"Lnk",
+ "advRouter":"6.6.6.6"
+ },
+ {
+ "type":"Lnk",
+ "advRouter":"7.7.7.7"
+ }
+ ]
+ },
+ {
+ "areaId":"3",
+ "interface":"lo",
+ "lsa":[
+ ]
+ }
+ ],
+ "asScopedLinkStateDb":[
+ {
+ "lsa":[
+ {
+ "type":"ASE",
+ "advRouter":"1.1.1.1",
+ "payload":"2001:db8:1000::1\/128"
+ },
+ {
+ "type":"ASE",
+ "advRouter":"7.7.7.7",
+ "payload":"2001:db8:1000::7\/128"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json
new file mode 100644
index 0000000..5548691
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_neighbor.json
@@ -0,0 +1,12 @@
+{
+ "neighbors":[
+ {
+ "neighborId":"6.6.6.6",
+ "priority":1,
+ "state":"Full",
+ "ifState":"PointToPoint",
+ "interfaceName":"eth-rt6",
+ "interfaceState":"PointToPoint"
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json
new file mode 100644
index 0000000..42ca54f
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_ospf_route.json
@@ -0,0 +1,74 @@
+{
+ "routes":{
+ "2001:db8:1000::1\/128":{
+ "isBestRoute":false,
+ "destinationType":"N",
+ "pathType":"E2",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::2\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::3\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::4\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::5\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::6\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IE",
+ "nextHops":[
+ {
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ },
+ "2001:db8:1000::7\/128":{
+ "isBestRoute":true,
+ "destinationType":"N",
+ "pathType":"IA",
+ "nextHops":[
+ {
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json
new file mode 100644
index 0000000..f5f8f71
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/show_ipv6_route.json
@@ -0,0 +1,139 @@
+{
+ "2001:db8:1000::1\/128":[
+ {
+ "prefix":"2001:db8:1000::1\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::2\/128":[
+ {
+ "prefix":"2001:db8:1000::2\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::3\/128":[
+ {
+ "prefix":"2001:db8:1000::3\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::4\/128":[
+ {
+ "prefix":"2001:db8:1000::4\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::5\/128":[
+ {
+ "prefix":"2001:db8:1000::5\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":50,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::6\/128":[
+ {
+ "prefix":"2001:db8:1000::6\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "afi":"ipv6",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2001:db8:1000::7\/128":[
+ {
+ "prefix":"2001:db8:1000::7\/128",
+ "protocol":"ospf6",
+ "vrfId":0,
+ "vrfName":"default",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf
new file mode 100644
index 0000000..3939472
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/rt7/zebra.conf
@@ -0,0 +1,22 @@
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+ ipv6 address 2001:db8:1000::7/128
+!
+interface stub1
+!
+interface eth-rt6
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
new file mode 100755
index 0000000..e59333e
--- /dev/null
+++ b/tests/topotests/ospf6_gr_topo1/test_ospf6_gr_topo1.py
@@ -0,0 +1,458 @@
+#!/usr/bin/env python
+
+#
+# test_ospf6_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 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_ospf6_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+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 (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+pytestmark = [pytest.mark.ospf6d]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def expect_grace_lsa(restarting, helper):
+ """
+ Check if the given helper neighbor has already received a Grace-LSA from
+ the router performing a graceful restart.
+ """
+ tgen = get_topogen()
+
+ logger.info(
+ "'{}': checking if a Grace-LSA was received from '{}'".format(
+ helper, restarting
+ )
+ )
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[helper],
+ "show ipv6 ospf6 database json",
+ {
+ "interfaceScopedLinkStateDb": [
+ {
+ "lsa": [
+ {
+ "type": "GR",
+ "advRouter": restarting,
+ }
+ ]
+ }
+ ]
+ },
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = '"{}" didn\'t receive a Grace-LSA from "{}"'.format(helper, restarting)
+
+ assert result is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ # If we are not on initial convergence *but* we are checking
+ # after a restart. Looking in the zebra rib for installed
+ # is a recipe for test failure. Why? because if we are restarting
+ # then ospf is in the process of establishing neighbors and passing
+ # new routes to zebra. Zebra will not mark the route as installed
+ # when it receives a replacement from ospf until it has finished
+ # processing it. Let's give it a few seconds to allow this to happen
+ # under load.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ if restarting != None:
+ tries = 40
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ipv6 route ospf json", "show_ipv6_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf neighbor json",
+ "show_ipv6_ospf_neighbor.json",
+ tries,
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname,
+ "show ipv6 ospf database json",
+ "show_ipv6_ospf_database.json",
+ tries,
+ )
+ router_compare_json_output(
+ rname, "show ipv6 ospf route json", "show_ipv6_ospf_route.json", tries
+ )
+
+
+def ensure_gr_is_in_zebra(rname):
+ retry = True
+ retry_times = 10
+ tgen = get_topogen()
+
+ while retry and retry_times > 0:
+ out = tgen.net[rname].cmd(
+ 'vtysh -c "show zebra client" | grep "Client: ospf6$" -A 40 | grep "Capabilities "'
+ )
+
+ if "Graceful Restart" not in out:
+ sleep(2)
+ retry_times -= 1
+ else:
+ retry = False
+
+ assertmsg = "%s does not appear to have Graceful Restart setup" % rname
+ assert not retry and retry_times > 0, assertmsg
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="1.1.1.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ kill_router_daemons(tgen, "rt1", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt1")
+ start_router_daemons(tgen, "rt1", ["ospf6d"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="2.2.2.2", helper="rt1")
+ expect_grace_lsa(restarting="2.2.2.2", helper="rt3")
+ ensure_gr_is_in_zebra("rt2")
+ kill_router_daemons(tgen, "rt2", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospf6d"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt2")
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt4")
+ expect_grace_lsa(restarting="3.3.3.3", helper="rt6")
+ ensure_gr_is_in_zebra("rt3")
+ kill_router_daemons(tgen, "rt3", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospf6d"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="4.4.4.4", helper="rt3")
+ expect_grace_lsa(restarting="4.4.4.4", helper="rt5")
+ ensure_gr_is_in_zebra("rt4")
+ kill_router_daemons(tgen, "rt4", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospf6d"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="5.5.5.5", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ kill_router_daemons(tgen, "rt5", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospf6d"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt3")
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt7")
+ ensure_gr_is_in_zebra("rt6")
+ kill_router_daemons(tgen, "rt6", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospf6d"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ipv6 ospf"')
+ expect_grace_lsa(restarting="6.6.6.6", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ kill_router_daemons(tgen, "rt7", ["ospf6d"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospf6d"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+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/ospf6_topo1/README.md b/tests/topotests/ospf6_topo1/README.md
new file mode 100644
index 0000000..526c019
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/README.md
@@ -0,0 +1,132 @@
+# OSPFv3 (IPv6) Topology Test
+
+## Topology
+ -----\
+ SW1 - Stub Net 1 SW2 - Stub Net 2 \
+ fc00:1:1:1::/64 fc00:2:2:2::/64 \
+ \___________________/ \___________________/ |
+ | | |
+ | | |
+ | ::1 | ::2 |
+ +---------+---------+ +---------+---------+ |
+ | R1 | | R2 | |
+ | FRRouting | | FRRouting | |
+ | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
+ +---------+---------+ +---------+---------+ |
+ | ::1 | ::2 \
+ \______ ___________/ OSPFv3
+ \ / Area 0.0.0.0
+ \ / /
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW5 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:A:A:A::/64 ~~ |
+ ~~~~~~~~~~~~~~~~~~ |
+ | /---- |
+ | ::3 | SW3 - Stub Net 3 |
+ +---------+---------+ /-+ fc00:3:3:3::/64 |
+ | R3 | / | /
+ | FRRouting +--/ \---- /
+ | Rtr-ID: 10.0.0.3 | ::3 ___________/
+ +---------+---------+ \
+ | ::3 \
+ | \
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW6 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:B:B:B::/64 ~~ \
+ ~~~~~~~~~~~~~~~~~~ OSPFv3
+ | Area 0.0.0.1
+ | ::4 /
+ +---------+---------+ /---- |
+ | R4 | | SW4 - Stub Net 4 |
+ | FRRouting +------+ fc00:4:4:4::/64 |
+ | Rtr-ID: 10.0.0.4 | ::4 | /
+ +-------------------+ \---- /
+ -----/
+
+## FRR Configuration
+
+Full config as used is in r1 / r2 / r3 / r4 / r5 subdirectories
+
+Simplified `R1` config (R1 is similar)
+
+ hostname r1
+ !
+ interface r1-stubnet
+ ipv6 address fc00:1:1:1::1/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r1-sw5
+ ipv6 address fc00:a:a:a::1/64
+ ipv6 ospf6 network broadcast
+ !
+ router ospf6
+ router-id 10.0.0.1
+ log-adjacency-changes detail
+ redistribute static
+ interface r1-stubnet area 0.0.0.0
+ interface r1-sw5 area 0.0.0.0
+ !
+ ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234
+
+Simplified `R3` config
+
+ hostname r3
+ !
+ interface r3-stubnet
+ ipv6 address fc00:3:3:3::3/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r3-sw5
+ ipv6 address fc00:a:a:a::3/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r3-sw6
+ ipv6 address fc00:b:b:b::3/64
+ ipv6 ospf6 network broadcast
+ !
+ router ospf6
+ router-id 10.0.0.3
+ log-adjacency-changes detail
+ redistribute static
+ interface r3-stubnet area 0.0.0.0
+ interface r3-sw5 area 0.0.0.0
+ interface r3-sw6 area 0.0.0.1
+ !
+ ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234
+
+## Tests executed
+
+### Check if FRR is running
+
+Test is executed by running
+
+ vtysh -c "show logging" | grep "Logging configuration for"
+
+on each FRR router. This should return the logging information for all daemons registered
+to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`)
+
+### Verify for OSPFv3 to converge
+
+OSPFv3 is expected to converge on each view within 60s total time. Convergence is verified by executing (on each node)
+
+ vtysh -c "show ipv6 ospf neigh"
+
+and checking for "Full" neighbor status in the output. An additional 15 seconds after the full converge is waited for routes to populate before the following routing table checks are executed
+
+### Verifying OSPFv3 Routing Tables
+
+Routing table is verified by running
+
+ vtysh -c "show ipv6 route"
+
+on each node and comparing the result to the stored example config (see `show_ipv6_route.ref` in r1 / r2 / r3 / r4 directories). Link-Local addresses are masked out before the compare.
+
+### Verifying Linux Kernel Routing Table
+
+Linux Kernel IPv6 Routing table is verified on each FRR node with
+
+ ip -6 route
+
+Tables are compared with reference routing table (see `ip_6_address.ref` in r1 / r2 / r3 / r4 directories). Link-Local addresses are translated after getting collected on each node with interface name to make them consistent
diff --git a/tests/topotests/ospf6_topo1/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6_topo1/r1/ip_6_address.nhg.ref
new file mode 100644
index 0000000..11fd9fe
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r1/ip_6_address.nhg.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1/r1/ip_6_address.ref b/tests/topotests/ospf6_topo1/r1/ip_6_address.ref
new file mode 100644
index 0000000..8c48f22
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r1/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1/r1/ospf6d.conf b/tests/topotests/ospf6_topo1/r1/ospf6d.conf
new file mode 100644
index 0000000..5f1ceee
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r1/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r1
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r1-stubnet
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r1-sw5
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.0.0.1
+ log-adjacency-changes detail
+ redistribute static
+ interface r1-stubnet area 0.0.0.0
+ interface r1-sw5 area 0.0.0.0
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref
new file mode 100644
index 0000000..96489b0
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r1/show_ipv6_route.ref
@@ -0,0 +1,9 @@
+O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1/r1/zebra.conf b/tests/topotests/ospf6_topo1/r1/zebra.conf
new file mode 100644
index 0000000..3a7db9f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r1/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r1
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r1-stubnet
+ ipv6 address fc00:1:1:1::1/64
+!
+interface r1-sw5
+ ipv6 address fc00:a:a:a::1/64
+!
+interface lo
+!
+ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1/r2/ip_6_address.nhg.ref b/tests/topotests/ospf6_topo1/r2/ip_6_address.nhg.ref
new file mode 100644
index 0000000..032acb5
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r2/ip_6_address.nhg.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1/r2/ip_6_address.ref b/tests/topotests/ospf6_topo1/r2/ip_6_address.ref
new file mode 100644
index 0000000..edb6c86
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r2/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1/r2/ospf6d.conf b/tests/topotests/ospf6_topo1/r2/ospf6d.conf
new file mode 100644
index 0000000..d51b41e
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r2/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r2
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r2-stubnet
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r2-sw5
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.0.0.2
+ log-adjacency-changes detail
+ redistribute static
+ interface r2-stubnet area 0.0.0.0
+ interface r2-sw5 area 0.0.0.0
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref
new file mode 100644
index 0000000..78c1ad8
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r2/show_ipv6_route.ref
@@ -0,0 +1,10 @@
+O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+
diff --git a/tests/topotests/ospf6_topo1/r2/zebra.conf b/tests/topotests/ospf6_topo1/r2/zebra.conf
new file mode 100644
index 0000000..5571dc9
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r2/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r2
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r2-stubnet
+ ipv6 address fc00:2:2:2::2/64
+!
+interface r2-sw5
+ ipv6 address fc00:a:a:a::2/64
+!
+interface lo
+!
+ipv6 route fc00:2222:2222:2222::/64 fc00:2:2:2::1234
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1/r3/ip_6_address.nhg.ref b/tests/topotests/ospf6_topo1/r3/ip_6_address.nhg.ref
new file mode 100644
index 0000000..101fcc9
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r3/ip_6_address.nhg.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 nhid XXXX via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1/r3/ip_6_address.ref b/tests/topotests/ospf6_topo1/r3/ip_6_address.ref
new file mode 100644
index 0000000..1a3a4ea
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r3/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1/r3/ospf6d.conf b/tests/topotests/ospf6_topo1/r3/ospf6d.conf
new file mode 100644
index 0000000..cad71ac
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r3/ospf6d.conf
@@ -0,0 +1,37 @@
+hostname r3
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r3-stubnet
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r3-sw5
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r3-sw6
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.0.0.3
+ log-adjacency-changes detail
+ redistribute static
+ interface r3-stubnet area 0.0.0.0
+ interface r3-sw5 area 0.0.0.0
+ interface r3-sw6 area 0.0.0.1
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref
new file mode 100644
index 0000000..dc0acbe
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r3/show_ipv6_route.ref
@@ -0,0 +1,10 @@
+O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX
+O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
+
diff --git a/tests/topotests/ospf6_topo1/r3/zebra.conf b/tests/topotests/ospf6_topo1/r3/zebra.conf
new file mode 100644
index 0000000..3cc5626
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r3/zebra.conf
@@ -0,0 +1,23 @@
+!
+hostname r3
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r3-stubnet
+ ipv6 address fc00:3:3:3::3/64
+!
+interface r3-sw5
+ ipv6 address fc00:a:a:a::3/64
+!
+interface r3-sw6
+ ipv6 address fc00:b:b:b::3/64
+!
+interface lo
+!
+ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1/r4/ip_6_address.nhg.ref b/tests/topotests/ospf6_topo1/r4/ip_6_address.nhg.ref
new file mode 100644
index 0000000..4f11670
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r4/ip_6_address.nhg.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium
+fc00:a:a:a::/64 nhid XXXX via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1/r4/ip_6_address.ref b/tests/topotests/ospf6_topo1/r4/ip_6_address.ref
new file mode 100644
index 0000000..cb3b745
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r4/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium
+fc00:a:a:a::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1/r4/ospf6d.conf b/tests/topotests/ospf6_topo1/r4/ospf6d.conf
new file mode 100644
index 0000000..f0b166b
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r4/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r4
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r4-stubnet
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r4-sw6
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.0.0.4
+ log-adjacency-changes detail
+ redistribute static
+ interface r4-stubnet area 0.0.0.1
+ interface r4-sw6 area 0.0.0.1
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref b/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref
new file mode 100644
index 0000000..730fd9f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r4/show_ipv6_route.ref
@@ -0,0 +1,9 @@
+O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX
+O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1/r4/zebra.conf b/tests/topotests/ospf6_topo1/r4/zebra.conf
new file mode 100644
index 0000000..20e27ce
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/r4/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r4
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r4-stubnet
+ ipv6 address fc00:4:4:4::4/64
+!
+interface r4-sw6
+ ipv6 address fc00:b:b:b::4/64
+!
+interface lo
+!
+ipv6 route fc00:4444:4444:4444::/64 fc00:4:4:4::1234
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1/test_ospf6_topo1.py b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
new file mode 100644
index 0000000..53d84e6
--- /dev/null
+++ b/tests/topotests/ospf6_topo1/test_ospf6_topo1.py
@@ -0,0 +1,435 @@
+#!/usr/bin/env python
+
+#
+# test_ospf6_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2016 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.
+#
+
+r"""
+test_ospf6_topo1.py:
+
+ -----\
+ SW1 - Stub Net 1 SW2 - Stub Net 2 \
+ fc00:1:1:1::/64 fc00:2:2:2::/64 \
+\___________________/ \___________________/ |
+ | | |
+ | | |
+ | ::1 | ::2 |
++---------+---------+ +---------+---------+ |
+| R1 | | R2 | |
+| FRRouting | | FRRouting | |
+| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
++---------+---------+ +---------+---------+ |
+ | ::1 | ::2 \
+ \______ ___________/ OSPFv3
+ \ / Area 0.0.0.0
+ \ / /
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW5 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:A:A:A::/64 ~~ |
+ ~~~~~~~~~~~~~~~~~~ |
+ | /---- |
+ | ::3 | SW3 - Stub Net 3 |
+ +---------+---------+ /-+ fc00:3:3:3::/64 |
+ | R3 | / | /
+ | FRRouting +--/ \---- /
+ | Rtr-ID: 10.0.0.3 | ::3 ___________/
+ +---------+---------+ \
+ | ::3 \
+ | \
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW6 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:B:B:B::/64 ~~ \
+ ~~~~~~~~~~~~~~~~~~ OSPFv3
+ | Area 0.0.0.1
+ | ::4 /
+ +---------+---------+ /---- |
+ | R4 | | SW4 - Stub Net 4 |
+ | FRRouting +------+ fc00:4:4:4::/64 |
+ | Rtr-ID: 10.0.0.4 | ::4 | /
+ +-------------------+ \---- /
+ -----/
+"""
+
+import os
+import re
+import sys
+import pytest
+
+from functools import partial
+
+
+# Save the Current Working Directory to find configuration files later.
+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
+
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ #
+ # Wire up the switches and routers
+ # Note that we specify the link names so we match the config files
+ #
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet")
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet")
+
+ # Create a empty network for router 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet")
+
+ # Create a empty network for router 4
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet")
+
+ # Interconnect routers 1, 2, and 3
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"], nodeif="r1-sw5")
+ switch.add_link(tgen.gears["r2"], nodeif="r2-sw5")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-sw5")
+
+ # Interconnect routers 3 and 4
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-sw6")
+ switch.add_link(tgen.gears["r4"], nodeif="r4-sw6")
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ logger.info("** %s: Setup Topology" % mod.__name__)
+ logger.info("******************************************")
+
+ # For debugging after starting net, but before starting FRR,
+ # uncomment the next line
+ # tgen.mininet_cli()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_wait_protocol_convergence():
+ "Wait for OSPFv3 to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_neighbor_full(router, neighbor):
+ "Wait until OSPFv3 convergence."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 neighbor json",
+ {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_neighbor_full("r1", "10.0.0.2")
+ expect_neighbor_full("r1", "10.0.0.3")
+
+ expect_neighbor_full("r2", "10.0.0.1")
+ expect_neighbor_full("r2", "10.0.0.3")
+
+ expect_neighbor_full("r3", "10.0.0.1")
+ expect_neighbor_full("r3", "10.0.0.2")
+ expect_neighbor_full("r3", "10.0.0.4")
+
+ expect_neighbor_full("r4", "10.0.0.3")
+
+
+def compare_show_ipv6(rname, expected):
+ """
+ Calls 'show ipv6 route' for router `rname` and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+
+ # Use the vtysh output, with some masking to make comparison easy
+ current = topotest.ip6_route_zebra(tgen.gears[rname])
+
+ # Use just the 'O'spf lines of the output
+ linearr = []
+ for line in current.splitlines():
+ if re.match("^O", line):
+ linearr.append(line)
+
+ current = "\n".join(linearr)
+
+ return topotest.difflines(
+ topotest.normalize_text(current),
+ topotest.normalize_text(expected),
+ title1="Current output",
+ title2="Expected output",
+ )
+
+
+def test_ospfv3_routingTable():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_linux_ipv6_kernel_routingTable():
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Verify Linux Kernel Routing Table
+ logger.info("Verifying Linux IPv6 Kernel Routing Table")
+
+ failures = 0
+
+ # Get a list of all current link-local addresses first as they change for
+ # each run and we need to translate them
+ linklocals = []
+ for i in range(1, 5):
+ linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal()
+
+ # Now compare the routing tables (after substituting link-local addresses)
+
+ for i in range(1, 5):
+ # Actual output from router
+ actual = tgen.gears["r{}".format(i)].run("ip -6 route").rstrip()
+ if "nhid" in actual:
+ refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
+ else:
+ refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i))
+
+ if os.path.isfile(refTableFile):
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines())).splitlines(1)
+
+ # Mask out Link-Local mac addresses
+ for ll in linklocals:
+ actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
+ # Mask out protocol name or number
+ actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
+ actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
+ # Remove ff00::/8 routes (seen on some kernels - not from FRR)
+ actual = re.sub(r"ff00::/8.*", "", actual)
+
+ # Strip empty lines
+ actual = actual.lstrip()
+ actual = actual.rstrip()
+ actual = re.sub(r" +", " ", actual)
+
+ filtered_lines = []
+ for line in sorted(actual.splitlines()):
+ if line.startswith("fe80::/64 ") or line.startswith(
+ "unreachable fe80::/64 "
+ ):
+ continue
+ filtered_lines.append(line)
+ actual = "\n".join(filtered_lines).splitlines(1)
+
+ # Print Actual table
+ # logger.info("Router r%s table" % i)
+ # for line in actual:
+ # logger.info(line.rstrip())
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual OSPFv3 IPv6 routing table",
+ title2="expected OSPFv3 IPv6 routing table",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n"
+ % (i, diff)
+ )
+ failures += 1
+ else:
+ logger.info("r%s ok" % i)
+
+ assert failures == 0, (
+ "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s"
+ % (i, diff)
+ )
+ else:
+ logger.error("r{} failed - no nhid ref file: {}".format(i, refTableFile))
+
+ assert False, (
+ "Linux Kernel IPv6 Routing Table verification failed for router r%s\n"
+ % (i)
+ )
+
+
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6\n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_shutdown_check_stderr():
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ logger.info(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
+ net = tgen.net
+
+ logger.info("\n\n** Verifying unexpected STDERR output from daemons")
+ logger.info("******************************************")
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ log = net["r%s" % i].getStdErr("ospf6d")
+ if log:
+ logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log))
+ log = net["r%s" % i].getStdErr("zebra")
+ if log:
+ logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
+
+
+def test_shutdown_check_memleak():
+ "Run the memory leak test and report results."
+
+ if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
+ logger.info(
+ "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)"
+ )
+ pytest.skip("Skipping test for memory leaks")
+
+ tgen = get_topogen()
+
+ net = tgen.net
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ net["r%s" % i].report_memory_leaks(
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
+ )
+
+
+if __name__ == "__main__":
+
+ # To suppress tracebacks, either use the following pytest call or
+ # add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/ospf6_topo1_vrf/README.md b/tests/topotests/ospf6_topo1_vrf/README.md
new file mode 100644
index 0000000..18bca5c
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/README.md
@@ -0,0 +1,132 @@
+# OSPFv3 (IPv6) Topology Test
+
+## Topology
+ -----\
+ SW1 - Stub Net 1 SW2 - Stub Net 2 \
+ fc00:1:1:1::/64 fc00:2:2:2::/64 \
+ \___________________/ \___________________/ |
+ | | |
+ | | |
+ | ::1 | ::2 |
+ +---------+---------+ +---------+---------+ |
+ | R1 | | R2 | |
+ | FRRouting | | FRRouting | |
+ | Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
+ +---------+---------+ +---------+---------+ |
+ | ::1 | ::2 \
+ \______ ___________/ OSPFv3
+ \ / Area 0.0.0.0
+ \ / /
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW5 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:A:A:A::/64 ~~ |
+ ~~~~~~~~~~~~~~~~~~ |
+ | /---- |
+ | ::3 | SW3 - Stub Net 3 |
+ +---------+---------+ /-+ fc00:3:3:3::/64 |
+ | R3 | / | /
+ | FRRouting +--/ \---- /
+ | Rtr-ID: 10.0.0.3 | ::3 ___________/
+ +---------+---------+ \
+ | ::3 \
+ | \
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW6 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:B:B:B::/64 ~~ \
+ ~~~~~~~~~~~~~~~~~~ OSPFv3
+ | Area 0.0.0.1
+ | ::4 /
+ +---------+---------+ /---- |
+ | R4 | | SW4 - Stub Net 4 |
+ | FRRouting +------+ fc00:4:4:4::/64 |
+ | Rtr-ID: 10.0.0.4 | ::4 | /
+ +-------------------+ \---- /
+ -----/
+
+## FRR Configuration
+
+Full config as used is in r1 / r2 / r3 / r4 / r5 subdirectories
+
+Simplified `R1` config (R1 is similar)
+
+ hostname r1
+ !
+ interface r1-stubnet vrf r1-cust1
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 address fc00:1:1:1::1/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r1-sw5 vrf r1-cust1
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 address fc00:a:a:a::1/64
+ ipv6 ospf6 network broadcast
+ !
+ router ospf6 vrf r1-cust1
+ router-id 10.0.0.1
+ log-adjacency-changes detail
+ redistribute static
+ !
+ ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 vrf r1-cust1
+
+Simplified `R3` config
+
+ hostname r3
+ !
+ interface r3-stubnet vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 address fc00:3:3:3::3/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r3-sw5 vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 address fc00:a:a:a::3/64
+ ipv6 ospf6 network broadcast
+ !
+ interface r3-sw6 vrf r3-cust1
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 address fc00:b:b:b::3/64
+ ipv6 ospf6 network broadcast
+ !
+ router ospf6 vrf r3-cust1
+ router-id 10.0.0.3
+ log-adjacency-changes detail
+ redistribute static
+ !
+ ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 vrf r3-cust1
+
+## Tests executed
+
+### Check if FRR is running
+
+Test is executed by running
+
+ vtysh -c "show logging" | grep "Logging configuration for"
+
+on each FRR router. This should return the logging information for all daemons registered
+to Zebra and the list of running daemons is compared to the daemons started for this test (`zebra` and `ospf6d`)
+
+### Verify for OSPFv3 to converge
+
+OSPFv3 is expected to converge on each view within 60s total time. Convergence is verified by executing (on each node)
+
+ vtysh -c "show ipv6 ospf vrf r1-cust1 neigh"
+
+and checking for "Full" neighbor status in the output. An additional 15 seconds after the full converge is waited for routes to populate before the following routing table checks are executed
+
+### Verifying OSPFv3 Routing Tables
+
+Routing table is verified by running
+
+ vtysh -c "show ipv6 route vrf r1-cust1"
+
+on each node and comparing the result to the stored example config (see `show_ipv6_route.ref` in r1 / r2 / r3 / r4 directories). Link-Local addresses are masked out before the compare.
+
+### Verifying Linux Kernel Routing Table
+
+Linux Kernel IPv6 Routing table is verified on each FRR node with
+
+ ip -6 route vrf r1-cust1
+
+Tables are compared with reference routing table (see `ip_6_address.ref` in r1 / r2 / r3 / r4 directories). Link-Local addresses are translated after getting collected on each node with interface name to make them consistent
diff --git a/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.nhg.ref b/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.nhg.ref
new file mode 100644
index 0000000..11fd9fe
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.nhg.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 nhid XXXX via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 nhid XXXX via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 nhid XXXX via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.ref b/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.ref
new file mode 100644
index 0000000..f17e1fe
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r1/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fc00:1:1:1::1234 dev r1-stubnet proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 dev r1-stubnet proto XXXX metric 256 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r1-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r1-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf
new file mode 100644
index 0000000..e60c599
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r1/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r1
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r1-stubnet
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r1-sw5
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6 vrf r1-cust1
+ ospf6 router-id 10.0.0.1
+ log-adjacency-changes detail
+ redistribute static
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref
new file mode 100644
index 0000000..96489b0
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r1/show_ipv6_vrf_route.ref
@@ -0,0 +1,9 @@
+O fc00:1:1:1::/64 [110/10] is directly connected, r1-stubnet, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r1-sw5, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1_vrf/r1/zebra.conf b/tests/topotests/ospf6_topo1_vrf/r1/zebra.conf
new file mode 100644
index 0000000..704912b
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r1/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r1
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r1-stubnet vrf r1-cust1
+ ipv6 address fc00:1:1:1::1/64
+!
+interface r1-sw5 vrf r1-cust1
+ ipv6 address fc00:a:a:a::1/64
+!
+interface lo
+!
+ipv6 route fc00:1111:1111:1111::/64 fc00:1:1:1::1234 vrf r1-cust1
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r2/ip_6_address.ref b/tests/topotests/ospf6_topo1_vrf/r2/ip_6_address.ref
new file mode 100644
index 0000000..1a3e67b
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r2/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fc00:2:2:2::1234 dev r2-stubnet proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 dev r2-stubnet proto XXXX metric 256 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r2-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 via fe80::__(r3-sw5)__ dev r2-sw5 proto XXXX metric 20 pref medium
diff --git a/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf
new file mode 100644
index 0000000..778c778
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r2/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r2
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r2-stubnet
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
+interface r2-sw5
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
+router ospf6 vrf r2-cust1
+ ospf6 router-id 10.0.0.2
+ log-adjacency-changes detail
+ redistribute static
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref
new file mode 100644
index 0000000..4c390f7
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r2/show_ipv6_vrf_route.ref
@@ -0,0 +1,9 @@
+O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O fc00:2:2:2::/64 [110/10] is directly connected, r2-stubnet, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:b:b:b::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r2-sw5, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1_vrf/r2/zebra.conf b/tests/topotests/ospf6_topo1_vrf/r2/zebra.conf
new file mode 100644
index 0000000..123d8c4
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r2/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r2
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r2-stubnet vrf r2-cust1
+ ipv6 address fc00:2:2:2::2/64
+!
+interface r2-sw5 vrf r2-cust1
+ ipv6 address fc00:a:a:a::2/64
+!
+interface lo
+!
+ipv6 route fc00:2222:2222:2222::/64 fc00:2:2:2::1234 vrf r2-cust1
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r3/ip_6_address.ref b/tests/topotests/ospf6_topo1_vrf/r3/ip_6_address.ref
new file mode 100644
index 0000000..d70027f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r3/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r1-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r2-sw5)__ dev r3-sw5 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fc00:3:3:3::1234 dev r3-stubnet proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 dev r3-stubnet proto XXXX metric 256 pref medium
+fc00:4444:4444:4444::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 via fe80::__(r4-sw6)__ dev r3-sw6 proto XXXX metric 20 pref medium
+fc00:a:a:a::/64 dev r3-sw5 proto XXXX metric 256 pref medium
+fc00:b:b:b::/64 dev r3-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf
new file mode 100644
index 0000000..61d183f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r3/ospf6d.conf
@@ -0,0 +1,37 @@
+hostname r3
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r3-stubnet
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
+interface r3-sw5
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
+interface r3-sw6
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
+router ospf6 vrf r3-cust1
+ ospf6 router-id 10.0.0.3
+ log-adjacency-changes detail
+ redistribute static
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref
new file mode 100644
index 0000000..989213f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r3/show_ipv6_vrf_route.ref
@@ -0,0 +1,9 @@
+O>* fc00:1:1:1::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O fc00:3:3:3::/64 [110/10] is directly connected, r3-stubnet, weight 1, XX:XX:XX
+O>* fc00:4:4:4::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
+O fc00:a:a:a::/64 [110/10] is directly connected, r3-sw5, weight 1, XX:XX:XX
+O fc00:b:b:b::/64 [110/10] is directly connected, r3-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw5, weight 1, XX:XX:XX
+O>* fc00:4444:4444:4444::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r3-sw6, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1_vrf/r3/zebra.conf b/tests/topotests/ospf6_topo1_vrf/r3/zebra.conf
new file mode 100644
index 0000000..bd6873d
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r3/zebra.conf
@@ -0,0 +1,23 @@
+!
+hostname r3
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r3-stubnet vrf r3-cust1
+ ipv6 address fc00:3:3:3::3/64
+!
+interface r3-sw5 vrf r3-cust1
+ ipv6 address fc00:a:a:a::3/64
+!
+interface r3-sw6 vrf r3-cust1
+ ipv6 address fc00:b:b:b::3/64
+!
+interface lo
+!
+ipv6 route fc00:3333:3333:3333::/64 fc00:3:3:3::1234 vrf r3-cust1
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r4/ip_6_address.ref b/tests/topotests/ospf6_topo1_vrf/r4/ip_6_address.ref
new file mode 100644
index 0000000..0883f3c
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r4/ip_6_address.ref
@@ -0,0 +1,10 @@
+fc00:1111:1111:1111::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:1:1:1::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2222:2222:2222::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:2:2:2::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3333:3333:3333::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:3:3:3::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:4444:4444:4444::/64 via fc00:4:4:4::1234 dev r4-stubnet proto XXXX metric 20 pref medium
+fc00:4:4:4::/64 dev r4-stubnet proto XXXX metric 256 pref medium
+fc00:a:a:a::/64 via fe80::__(r3-sw6)__ dev r4-sw6 proto XXXX metric 20 pref medium
+fc00:b:b:b::/64 dev r4-sw6 proto XXXX metric 256 pref medium
diff --git a/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf b/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf
new file mode 100644
index 0000000..12f6f52
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r4/ospf6d.conf
@@ -0,0 +1,31 @@
+hostname r4
+log file ospf6d.log
+!
+! debug ospf6 message all
+! debug ospf6 lsa unknown
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 route table
+! debug ospf6 flooding
+!
+interface r4-stubnet
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r4-sw6
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 network broadcast
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6 vrf r4-cust1
+ ospf6 router-id 10.0.0.4
+ log-adjacency-changes detail
+ redistribute static
+!
+line vty
+ exec-timeout 0 0
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref b/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref
new file mode 100644
index 0000000..730fd9f
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r4/show_ipv6_vrf_route.ref
@@ -0,0 +1,9 @@
+O>* fc00:1:1:1::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:2:2:2::/64 [110/30] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3:3:3::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O fc00:4:4:4::/64 [110/10] is directly connected, r4-stubnet, weight 1, XX:XX:XX
+O>* fc00:a:a:a::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O fc00:b:b:b::/64 [110/10] is directly connected, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:1111:1111:1111::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:2222:2222:2222::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
+O>* fc00:3333:3333:3333::/64 [110/20] via fe80::XXXX:XXXX:XXXX:XXXX, r4-sw6, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf6_topo1_vrf/r4/zebra.conf b/tests/topotests/ospf6_topo1_vrf/r4/zebra.conf
new file mode 100644
index 0000000..0d9d011
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/r4/zebra.conf
@@ -0,0 +1,20 @@
+!
+hostname r4
+log file zebra.log
+!
+! debug zebra events
+! debug zebra rib
+!
+interface r4-stubnet vrf r4-cust1
+ ipv6 address fc00:4:4:4::4/64
+!
+interface r4-sw6 vrf r4-cust1
+ ipv6 address fc00:b:b:b::4/64
+!
+interface lo
+!
+ipv6 route fc00:4444:4444:4444::/64 fc00:4:4:4::1234 vrf r4-cust1
+!
+!
+line vty
+!
diff --git a/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
new file mode 100755
index 0000000..f823d5a
--- /dev/null
+++ b/tests/topotests/ospf6_topo1_vrf/test_ospf6_topo1_vrf.py
@@ -0,0 +1,475 @@
+#!/usr/bin/env python
+
+#
+# test_ospf6_topo1_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Niral Networks, Inc. ("Niral Networks")
+# Used Copyright (c) 2016 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 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.
+#
+
+r"""
+test_ospf6_topo1_vrf.py:
+
+ -----\
+ SW1 - Stub Net 1 SW2 - Stub Net 2 \
+ fc00:1:1:1::/64 fc00:2:2:2::/64 \
+\___________________/ \___________________/ |
+ | | |
+ | | |
+ | ::1 | ::2 |
++---------+---------+ +---------+---------+ |
+| R1 | | R2 | |
+| FRRouting | | FRRouting | |
+| Rtr-ID: 10.0.0.1 | | Rtr-ID: 10.0.0.2 | |
++---------+---------+ +---------+---------+ |
+ | ::1 | ::2 \
+ \______ ___________/ OSPFv3
+ \ / Area 0.0.0.0
+ \ / /
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW5 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:A:A:A::/64 ~~ |
+ ~~~~~~~~~~~~~~~~~~ |
+ | /---- |
+ | ::3 | SW3 - Stub Net 3 |
+ +---------+---------+ /-+ fc00:3:3:3::/64 |
+ | R3 | / | /
+ | FRRouting +--/ \---- /
+ | Rtr-ID: 10.0.0.3 | ::3 ___________/
+ +---------+---------+ \
+ | ::3 \
+ | \
+ ~~~~~~~~~~~~~~~~~~ |
+ ~~ SW6 ~~ |
+ ~~ Switch ~~ |
+ ~~ fc00:B:B:B::/64 ~~ \
+ ~~~~~~~~~~~~~~~~~~ OSPFv3
+ | Area 0.0.0.1
+ | ::4 /
+ +---------+---------+ /---- |
+ | R4 | | SW4 - Stub Net 4 |
+ | FRRouting +------+ fc00:4:4:4::/64 |
+ | Rtr-ID: 10.0.0.4 | ::4 | /
+ +-------------------+ \---- /
+ -----/
+"""
+
+import os
+import re
+import sys
+import pytest
+
+from functools import partial
+
+
+# Save the Current Working Directory to find configuration files later.
+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.topotest import iproute2_is_vrf_capable
+from lib.common_config import required_linux_kernel_version
+
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ #
+ # Wire up the switches and routers
+ # Note that we specify the link names so we match the config files
+ #
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"], nodeif="r1-stubnet")
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"], nodeif="r2-stubnet")
+
+ # Create a empty network for router 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-stubnet")
+
+ # Create a empty network for router 4
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet")
+
+ # Interconnect routers 1, 2, and 3
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r1"], nodeif="r1-sw5")
+ switch.add_link(tgen.gears["r2"], nodeif="r2-sw5")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-sw5")
+
+ # Interconnect routers 3 and 4
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-sw6")
+ switch.add_link(tgen.gears["r4"], nodeif="r4-sw6")
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("5.0")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ logger.info("** %s: Setup Topology" % mod.__name__)
+ logger.info("******************************************")
+
+ # For debugging after starting net, but before starting FRR,
+ # uncomment the next line
+ # tgen.mininet_cli()
+
+ logger.info("Testing with VRF Lite support")
+
+ cmds = [
+ "ip link add {0}-cust1 type vrf table 1001",
+ "ip link add loop1 type dummy",
+ "ip link set {0}-stubnet master {0}-cust1",
+ ]
+
+ cmds1 = ["ip link set {0}-sw5 master {0}-cust1"]
+
+ cmds2 = ["ip link set {0}-sw6 master {0}-cust1"]
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ # create VRF rx-cust1 and link rx-eth0 to rx-cust1
+ for cmd in cmds:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+ if rname == "r1" or rname == "r2" or rname == "r3":
+ for cmd in cmds1:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+ if rname == "r3" or rname == "r4":
+ for cmd in cmds2:
+ output = tgen.net[rname].cmd(cmd.format(rname))
+
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_wait_protocol_convergence():
+ "Wait for OSPFv3 to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_neighbor_full(router, neighbor):
+ "Wait until OSPFv3 convergence."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 vrf {0}-cust1 neighbor json".format(router),
+ {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_neighbor_full("r1", "10.0.0.2")
+ expect_neighbor_full("r1", "10.0.0.3")
+
+ expect_neighbor_full("r2", "10.0.0.1")
+ expect_neighbor_full("r2", "10.0.0.3")
+
+ expect_neighbor_full("r3", "10.0.0.1")
+ expect_neighbor_full("r3", "10.0.0.2")
+ expect_neighbor_full("r3", "10.0.0.4")
+
+
+def compare_show_ipv6_vrf(rname, expected):
+ """
+ Calls 'show ipv6 route' for router `rname` and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+
+ # Use the vtysh output, with some masking to make comparison easy
+ vrf_name = "{0}-cust1".format(rname)
+ current = topotest.ip6_route_zebra(tgen.gears[rname], vrf_name)
+
+ # Use just the 'O'spf lines of the output
+ linearr = []
+ for line in current.splitlines():
+ if re.match("^O", line):
+ linearr.append(line)
+
+ current = "\n".join(linearr)
+
+ return topotest.difflines(
+ topotest.normalize_text(current),
+ topotest.normalize_text(expected),
+ title1="Current output",
+ title2="Expected output",
+ )
+
+
+def test_ospfv3_routingTable():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_vrf, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_linux_ipv6_kernel_routingTable():
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Verify Linux Kernel Routing Table
+ logger.info("Verifying Linux IPv6 Kernel Routing Table")
+
+ failures = 0
+
+ # Get a list of all current link-local addresses first as they change for
+ # each run and we need to translate them
+ linklocals = []
+ for i in range(1, 5):
+ linklocals += tgen.net["r{}".format(i)].get_ipv6_linklocal()
+
+ # Now compare the routing tables (after substituting link-local addresses)
+
+ for i in range(1, 5):
+ # Actual output from router
+ actual = (
+ tgen.gears["r{}".format(i)]
+ .run("ip -6 route show vrf r{}-cust1".format(i))
+ .rstrip()
+ )
+ if "nhid" in actual:
+ refTableFile = os.path.join(CWD, "r{}/ip_6_address.nhg.ref".format(i))
+ else:
+ refTableFile = os.path.join(CWD, "r{}/ip_6_address.ref".format(i))
+
+ if os.path.isfile(refTableFile):
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines())).splitlines(1)
+
+ # Mask out Link-Local mac addresses
+ for ll in linklocals:
+ actual = actual.replace(ll[1], "fe80::__(%s)__" % ll[0])
+ # Mask out protocol name or number
+ actual = re.sub(r"[ ]+proto [0-9a-z]+ +", " proto XXXX ", actual)
+ actual = re.sub(r"[ ]+nhid [0-9]+ +", " nhid XXXX ", actual)
+ # Remove ff00::/8 routes (seen on some kernels - not from FRR)
+ actual = re.sub(r"ff00::/8.*", "", actual)
+
+ # Strip empty lines
+ actual = actual.lstrip()
+ actual = actual.rstrip()
+ actual = re.sub(r" +", " ", actual)
+
+ filtered_lines = []
+ for line in sorted(actual.splitlines()):
+ if line.startswith("fe80::/64 ") or line.startswith(
+ "unreachable fe80::/64 "
+ ):
+ continue
+ if "anycast" in line:
+ continue
+ if "multicast" in line:
+ continue
+ filtered_lines.append(line)
+ actual = "\n".join(filtered_lines).splitlines(1)
+
+ # Print Actual table
+ # logger.info("Router r%s table" % i)
+ # for line in actual:
+ # logger.info(line.rstrip())
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual OSPFv3 IPv6 routing table",
+ title2="expected OSPFv3 IPv6 routing table",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed Linux IPv6 Kernel Routing Table Check:\n%s\n"
+ % (i, diff)
+ )
+ failures += 1
+ else:
+ logger.info("r%s ok" % i)
+
+ assert failures == 0, (
+ "Linux Kernel IPv6 Routing Table verification failed for router r%s:\n%s"
+ % (i, diff)
+ )
+
+
+def test_ospfv3_routingTable_write_multiplier():
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # For debugging, uncomment the next line
+ # tgen.mininet_cli()
+ # Modify R1 write muliplier and reset the interfaces
+ r1 = tgen.gears["r1"]
+
+ r1.vtysh_cmd("conf t\nrouter ospf6 vrf r1-cust1 \n write-multiplier 100")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-stubnet")
+ r1.vtysh_cmd("clear ipv6 ospf interface r1-sw5")
+
+ # Verify OSPFv3 Routing Table
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/show_ipv6_vrf_route.ref".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_vrf, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=120, wait=0.5)
+ assert result, "OSPFv3 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_shutdown_check_stderr():
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ logger.info(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
+ net = tgen.net
+
+ logger.info("\n\n** Verifying unexpected STDERR output from daemons")
+ logger.info("******************************************")
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ log = net["r%s" % i].getStdErr("ospf6d")
+ if log:
+ logger.info("\nRouter r%s OSPF6d StdErr Log:\n%s" % (i, log))
+ log = net["r%s" % i].getStdErr("zebra")
+ if log:
+ logger.info("\nRouter r%s Zebra StdErr Log:\n%s" % (i, log))
+
+
+def test_shutdown_check_memleak():
+ "Run the memory leak test and report results."
+
+ if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
+ logger.info(
+ "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)"
+ )
+ pytest.skip("Skipping test for memory leaks")
+
+ tgen = get_topogen()
+
+ net = tgen.net
+
+ for i in range(1, 5):
+ net["r%s" % i].stopRouter()
+ net["r%s" % i].report_memory_leaks(
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
+ )
+
+
+if __name__ == "__main__":
+
+ # To suppress tracebacks, either use the following pytest call or
+ # add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/ospf6_topo2/r1/ospf6d.conf b/tests/topotests/ospf6_topo2/r1/ospf6d.conf
new file mode 100644
index 0000000..a9d46be
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r1/ospf6d.conf
@@ -0,0 +1,37 @@
+! debug ospf6 lsa router
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router examine
+! debug ospf6 lsa router flooding
+! debug ospf6 lsa nssa
+! debug ospf6 lsa nssa originate
+! debug ospf6 lsa nssa examine
+! debug ospf6 lsa nssa flooding
+! debug ospf6 lsa as-external
+! debug ospf6 lsa as-external originate
+! debug ospf6 lsa as-external examine
+! debug ospf6 lsa as-external flooding
+! debug ospf6 lsa intra-prefix
+! debug ospf6 lsa intra-prefix originate
+! debug ospf6 lsa intra-prefix examine
+! debug ospf6 lsa intra-prefix flooding
+! debug ospf6 border-routers
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 spf process
+! debug ospf6 route intra-area
+! debug ospf6 route inter-area
+! debug ospf6 abr
+! debug ospf6 asbr
+! debug ospf6 nssa
+!
+interface r1-eth0
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.1
+ area 0.0.0.1 stub
+!
diff --git a/tests/topotests/ospf6_topo2/r1/zebra.conf b/tests/topotests/ospf6_topo2/r1/zebra.conf
new file mode 100644
index 0000000..7fee2da
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r1/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface r1-eth0
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/ospf6_topo2/r2/ospf6d.conf b/tests/topotests/ospf6_topo2/r2/ospf6d.conf
new file mode 100644
index 0000000..8819a58
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r2/ospf6d.conf
@@ -0,0 +1,51 @@
+! debug ospf6 lsa router
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router examine
+! debug ospf6 lsa router flooding
+! debug ospf6 lsa nssa
+! debug ospf6 lsa nssa originate
+! debug ospf6 lsa nssa examine
+! debug ospf6 lsa nssa flooding
+! debug ospf6 lsa as-external
+! debug ospf6 lsa as-external originate
+! debug ospf6 lsa as-external examine
+! debug ospf6 lsa as-external flooding
+! debug ospf6 lsa intra-prefix
+! debug ospf6 lsa intra-prefix originate
+! debug ospf6 lsa intra-prefix examine
+! debug ospf6 lsa intra-prefix flooding
+! debug ospf6 border-routers
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 spf process
+! debug ospf6 route intra-area
+! debug ospf6 route inter-area
+! debug ospf6 abr
+! debug ospf6 asbr
+! debug ospf6 nssa
+!
+interface r2-eth0
+ ipv6 ospf6 area 0.0.0.1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r2-eth1
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+interface r2-eth2
+ ipv6 ospf6 area 0.0.0.2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.2
+ redistribute connected
+ redistribute static
+ default-information originate always metric 123
+ area 0.0.0.1 stub
+ area 0.0.0.2 nssa
+!
diff --git a/tests/topotests/ospf6_topo2/r2/zebra.conf b/tests/topotests/ospf6_topo2/r2/zebra.conf
new file mode 100644
index 0000000..559f502
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r2/zebra.conf
@@ -0,0 +1,11 @@
+ipv6 forwarding
+!
+interface r2-eth0
+ ipv6 address 2001:db8:1::1/64
+!
+interface r2-eth1
+ ipv6 address 2001:db8:2::2/64
+!
+interface r2-eth2
+ ipv6 address 2001:db8:3::1/64
+!
diff --git a/tests/topotests/ospf6_topo2/r3/ospf6d.conf b/tests/topotests/ospf6_topo2/r3/ospf6d.conf
new file mode 100644
index 0000000..6e4f56b
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r3/ospf6d.conf
@@ -0,0 +1,38 @@
+! debug ospf6 lsa router
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router examine
+! debug ospf6 lsa router flooding
+! debug ospf6 lsa nssa
+! debug ospf6 lsa nssa originate
+! debug ospf6 lsa nssa examine
+! debug ospf6 lsa nssa flooding
+! debug ospf6 lsa as-external
+! debug ospf6 lsa as-external originate
+! debug ospf6 lsa as-external examine
+! debug ospf6 lsa as-external flooding
+! debug ospf6 lsa intra-prefix
+! debug ospf6 lsa intra-prefix originate
+! debug ospf6 lsa intra-prefix examine
+! debug ospf6 lsa intra-prefix flooding
+! debug ospf6 border-routers
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 spf process
+! debug ospf6 route intra-area
+! debug ospf6 route inter-area
+! debug ospf6 abr
+! debug ospf6 asbr
+! debug ospf6 nssa
+!
+interface r3-eth0
+ ipv6 ospf6 area 0.0.0.0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.3
+ redistribute connected
+ redistribute static
+!
diff --git a/tests/topotests/ospf6_topo2/r3/zebra.conf b/tests/topotests/ospf6_topo2/r3/zebra.conf
new file mode 100644
index 0000000..dea2fe4
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r3/zebra.conf
@@ -0,0 +1,8 @@
+ipv6 forwarding
+!
+interface r3-eth0
+ ipv6 address 2001:db8:2::1/64
+!
+ipv6 route fc00:1::/64 fc00:100::1234
+ipv6 route fc00:2::/64 fc00:100::1234
+ipv6 route fc00:3::/64 fc00:100::1234
diff --git a/tests/topotests/ospf6_topo2/r4/ospf6d.conf b/tests/topotests/ospf6_topo2/r4/ospf6d.conf
new file mode 100644
index 0000000..59a2fd5
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r4/ospf6d.conf
@@ -0,0 +1,37 @@
+! debug ospf6 lsa router
+! debug ospf6 lsa router originate
+! debug ospf6 lsa router examine
+! debug ospf6 lsa router flooding
+! debug ospf6 lsa nssa
+! debug ospf6 lsa nssa originate
+! debug ospf6 lsa nssa examine
+! debug ospf6 lsa nssa flooding
+! debug ospf6 lsa as-external
+! debug ospf6 lsa as-external originate
+! debug ospf6 lsa as-external examine
+! debug ospf6 lsa as-external flooding
+! debug ospf6 lsa intra-prefix
+! debug ospf6 lsa intra-prefix originate
+! debug ospf6 lsa intra-prefix examine
+! debug ospf6 lsa intra-prefix flooding
+! debug ospf6 border-routers
+! debug ospf6 zebra
+! debug ospf6 interface
+! debug ospf6 neighbor
+! debug ospf6 flooding
+! debug ospf6 spf process
+! debug ospf6 route intra-area
+! debug ospf6 route inter-area
+! debug ospf6 abr
+! debug ospf6 asbr
+! debug ospf6 nssa
+!
+interface r4-eth0
+ ipv6 ospf6 area 0.0.0.2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 router-id 10.254.254.4
+ area 0.0.0.2 nssa
+!
diff --git a/tests/topotests/ospf6_topo2/r4/zebra.conf b/tests/topotests/ospf6_topo2/r4/zebra.conf
new file mode 100644
index 0000000..86cb972
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/r4/zebra.conf
@@ -0,0 +1,5 @@
+ipv6 forwarding
+!
+interface r4-eth0
+ ipv6 address 2001:db8:3::2/64
+!
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot b/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot
new file mode 100644
index 0000000..238ec7a
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.dot
@@ -0,0 +1,83 @@
+## 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="ospf6-topo2";
+
+ # 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\n2001:db8:1::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n2001:db8:2::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw3 [
+ shape=oval,
+ label="sw3\n2001:db8:3::/64",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="area 0.0.0.1";
+ r1 -- sw1 [label="eth0\n.2"];
+ }
+
+ subgraph cluster1 {
+ label="area 0.0.0.2";
+ r4 -- sw3 [label="eth0\n.2"];
+ }
+
+ subgraph cluster2 {
+ label="area 0.0.0.0";
+ r2 -- sw1 [label="eth0\n.1"];
+ r2 -- sw2 [label="eth1\n.2"];
+ r2 -- sw3 [label="eth2\n.1"];
+
+ r3 -- sw2 [label="eth0\n.1"];
+ }
+}
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.png b/tests/topotests/ospf6_topo2/test_ospf6_topo2.png
new file mode 100644
index 0000000..4e79559
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.png
Binary files differ
diff --git a/tests/topotests/ospf6_topo2/test_ospf6_topo2.py b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
new file mode 100644
index 0000000..d17aeda
--- /dev/null
+++ b/tests/topotests/ospf6_topo2/test_ospf6_topo2.py
@@ -0,0 +1,665 @@
+#!/usr/bin/env python
+
+#
+# test_ospf6_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 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_ospf6_topo2.py: Test the FRR OSPFv3 daemon.
+"""
+
+import os
+import sys
+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.
+
+pytestmark = [pytest.mark.ospf6d]
+
+
+def expect_lsas(router, area, lsas, wait=5, extra_params=""):
+ """
+ Run the OSPFv3 show LSA database command and expect the supplied LSAs.
+
+ Optional parameters:
+ * `wait`: amount of seconds to wait.
+ * `extra_params`: extra LSA database parameters.
+ * `inverse`: assert the inverse of the expected.
+ """
+ tgen = get_topogen()
+
+ command = "show ipv6 ospf6 database {} json".format(extra_params)
+
+ logger.info("waiting OSPFv3 router '{}' LSA".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ command,
+ {"areaScopedLinkStateDb": [{"areaId": area, "lsa": lsas}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
+def expect_ospfv3_routes(router, routes, wait=5, type=None, detail=False):
+ "Run command `ipv6 ospf6 route` and expect route with type."
+ tgen = get_topogen()
+
+ if detail == False:
+ if type == None:
+ cmd = "show ipv6 ospf6 route json"
+ else:
+ cmd = "show ipv6 ospf6 route {} json".format(type)
+ else:
+ if type == None:
+ cmd = "show ipv6 ospf6 route detail json"
+ else:
+ cmd = "show ipv6 ospf6 route {} detail json".format(type)
+
+ logger.info("waiting OSPFv3 router '{}' route".format(router))
+ test_func = partial(
+ topotest.router_json_cmp, tgen.gears[router], cmd, {"routes": routes}
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=wait, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+
+ assert result is None, assertmsg
+
+
+def dont_expect_route(router, unexpected_route, type=None):
+ "Specialized test function to expect route go missing"
+ tgen = get_topogen()
+
+ if type == None:
+ cmd = "show ipv6 ospf6 route json"
+ else:
+ cmd = "show ipv6 ospf6 route {} json".format(type)
+
+ output = tgen.gears[router].vtysh_cmd(cmd, isjson=True)
+ if unexpected_route in output["routes"]:
+ return output["routes"][unexpected_route]
+ return None
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"], nodeif="r4-stubnet")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ daemon_file = "{}/{}/ospf6d.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_OSPF6, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def test_wait_protocol_convergence():
+ "Wait for OSPFv3 to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_neighbor_full(router, neighbor):
+ "Wait until OSPFv3 convergence."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 neighbor json",
+ {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_neighbor_full("r1", "10.254.254.2")
+ expect_neighbor_full("r2", "10.254.254.1")
+ expect_neighbor_full("r2", "10.254.254.3")
+ expect_neighbor_full("r2", "10.254.254.4")
+ expect_neighbor_full("r3", "10.254.254.2")
+ expect_neighbor_full("r4", "10.254.254.2")
+
+
+def test_ospfv3_expected_route_types():
+ "Test routers route type to determine if NSSA/Stub is working as expected."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_ospf6_route_types(router, expected_summary):
+ "Expect the correct route types."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 route summary json",
+ expected_summary,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Stub router: no external routes.
+ expect_ospf6_route_types(
+ "r1",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 3,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 0,
+ },
+ )
+ # NSSA router: no external routes.
+ expect_ospf6_route_types(
+ "r4",
+ {
+ "numberOfIntraAreaRoutes": 1,
+ "numberOfInterAreaRoutes": 2,
+ "numberOfExternal1Routes": 0,
+ "numberOfExternal2Routes": 3,
+ },
+ )
+
+
+def test_ospf6_default_route():
+ "Wait for OSPFv3 default route in stub area."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for default route")
+
+ def expect_route(router, route, metric):
+ "Test OSPF6 route existence."
+ logger.info("waiting OSPFv3 router '{}' routes".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 route json",
+ {route: [{"metric": metric}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=4, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ metric = 123
+ expect_lsas(
+ "r1",
+ "0.0.0.1",
+ [{"prefix": "::/0", "metric": metric}],
+ extra_params="inter-prefix detail",
+ )
+ expect_route("r1", "::/0", metric + 10)
+
+
+def test_redistribute_metrics():
+ """
+ Test that the configured metrics are honored when a static route is
+ redistributed.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Add new static route on r3.
+ config = """
+ configure terminal
+ ipv6 route 2001:db8:500::/64 Null0
+ """
+ tgen.gears["r3"].vtysh_cmd(config)
+
+ route = {
+ "2001:db8:500::/64": {
+ "metricType": 2,
+ "metricCost": 10,
+ }
+ }
+ logger.info(
+ "Expecting AS-external route 2001:db8:500::/64 to show up with default metrics"
+ )
+ expect_ospfv3_routes("r2", route, wait=30, detail=True)
+
+ # Change the metric of redistributed routes of the static type on r3.
+ config = """
+ configure terminal
+ router ospf6
+ redistribute static metric 50 metric-type 1
+ """
+ tgen.gears["r3"].vtysh_cmd(config)
+
+ # Check if r3 reinstalled 2001:db8:500::/64 using the new metric type and value.
+ route = {
+ "2001:db8:500::/64": {
+ "metricType": 1,
+ "metricCost": 60,
+ }
+ }
+ logger.info(
+ "Expecting AS-external route 2001:db8:500::/64 to show up with updated metric type and value"
+ )
+ expect_ospfv3_routes("r2", route, wait=30, detail=True)
+
+
+def test_nssa_lsa_type7():
+ """
+ Test that static route gets announced as external route when redistributed
+ and gets removed when redistribution stops.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Add new static route and check if it gets announced as LSA Type-7.
+ #
+ config = """
+ configure terminal
+ ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ lsas = [
+ {
+ "type": "NSSA",
+ "advertisingRouter": "10.254.254.2",
+ "prefix": "2001:db8:100::/64",
+ "forwardingAddress": "2001:db8:3::1",
+ }
+ ]
+ route = {
+ "2001:db8:100::/64": {
+ "pathType": "E2",
+ "nextHops": [{"nextHop": "::", "interfaceName": "r4-eth0"}],
+ }
+ }
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to show up")
+ expect_lsas("r4", "0.0.0.2", lsas, wait=30, extra_params="type-7 detail")
+ expect_ospfv3_routes("r4", route, wait=30)
+
+ #
+ # Remove static route and check for LSA Type-7 removal.
+ #
+ config = """
+ configure terminal
+ no ipv6 route 2001:db8:100::/64 Null0
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ def dont_expect_lsa(unexpected_lsa):
+ "Specialized test function to expect LSA go missing"
+ output = tgen.gears["r4"].vtysh_cmd(
+ "show ipv6 ospf6 database type-7 detail json", isjson=True
+ )
+ for lsa in output["areaScopedLinkStateDb"][0]["lsa"]:
+ if lsa["prefix"] == unexpected_lsa["prefix"]:
+ if lsa["forwardingAddress"] == unexpected_lsa["forwardingAddress"]:
+ return lsa
+ return None
+
+ logger.info("Expecting LSA type-7 and OSPFv3 route 2001:db8:100::/64 to go away")
+
+ # Test that LSA doesn't exist.
+ test_func = partial(dont_expect_lsa, lsas[0])
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" LSA still exists'.format("r4")
+ assert result is None, assertmsg
+
+ # Test that route doesn't exist.
+ test_func = partial(dont_expect_route, "r4", "2001:db8:100::/64")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" route still exists'.format("r4")
+ assert result is None, assertmsg
+
+
+def test_nssa_no_summary():
+ """
+ Test the following:
+ * Type-3 inter-area routes should be removed when the NSSA no-summary option
+ is configured;
+ * A type-3 inter-area default route should be originated into the NSSA area
+ when the no-summary option is configured;
+ * Once the no-summary option is unconfigured, all previously existing
+ Type-3 inter-area routes should be re-added, and the inter-area default
+ route removed.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure area 1 as a NSSA totally stub area.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa no-summary
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting inter-area routes to be removed")
+ for route in ["2001:db8:1::/64", "2001:db8:2::/64"]:
+ test_func = partial(dont_expect_route, "r4", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r4", route)
+ assert result is None, assertmsg
+
+ logger.info("Expecting inter-area default-route to be added")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r4", routes, wait=30, type="inter-area")
+
+ #
+ # Configure area 1 as a regular NSSA area.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting inter-area routes to be re-added")
+ routes = {"2001:db8:1::/64": {}, "2001:db8:2::/64": {}}
+ expect_ospfv3_routes("r4", routes, wait=30, type="inter-area")
+
+ logger.info("Expecting inter-area default route to be removed")
+ test_func = partial(dont_expect_route, "r4", "::/0", type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s inter-area default route still exists".format("r4")
+ assert result is None, assertmsg
+
+
+def test_nssa_default_originate():
+ """
+ Test the following:
+ * A type-7 default route should be originated into the NSSA area
+ when the default-information-originate option is configured;
+ * Once the default-information-originate option is unconfigured, the
+ previously originated Type-7 default route should be removed.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure r2 to announce a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no default-information originate
+ area 2 nssa default-information-originate
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default-route to be added")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r4", routes, wait=30, type="external-2")
+
+ #
+ # Configure r2 to stop announcing a Type-7 default route.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting Type-7 default route to be removed")
+ test_func = partial(dont_expect_route, "r4", "::/0", type="external-2")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "r4's Type-7 default route still exists"
+ assert result is None, assertmsg
+
+
+def test_area_filters():
+ """
+ Test ABR import/export filters.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ #
+ # Configure import/export filters on r2 (ABR for area 2).
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 5 permit 2001:db8:2::/64
+ ipv6 access-list ACL_IMPORT seq 10 deny any
+ ipv6 access-list ACL_EXPORT seq 10 deny any
+ router ospf6
+ area 1 import-list ACL_IMPORT
+ area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting inter-area routes to be removed on r1")
+ for route in ["::/0", "2001:db8:3::/64"]:
+ test_func = partial(dont_expect_route, "r1", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r1", route)
+ assert result is None, assertmsg
+
+ logger.info("Expecting inter-area routes to be removed on r3")
+ for route in ["2001:db8:1::/64"]:
+ test_func = partial(dont_expect_route, "r3", route, type="inter-area")
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = "{}'s {} inter-area route still exists".format("r3", route)
+ assert result is None, assertmsg
+
+ #
+ # Update the ACLs used by the import/export filters.
+ #
+ config = """
+ configure terminal
+ ipv6 access-list ACL_IMPORT seq 6 permit 2001:db8:3::/64
+ ipv6 access-list ACL_EXPORT seq 5 permit 2001:db8:1::/64
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting 2001:db8:3::/64 to be re-added on r1")
+ routes = {"2001:db8:3::/64": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+ logger.info("Expecting 2001:db8:1::/64 to be re-added on r3")
+ routes = {"2001:db8:1::/64": {}}
+ expect_ospfv3_routes("r3", routes, wait=30, type="inter-area")
+
+ #
+ # Unconfigure r2's ABR import/export filters.
+ #
+ config = """
+ configure terminal
+ router ospf6
+ no area 1 import-list ACL_IMPORT
+ no area 1 export-list ACL_EXPORT
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+
+ logger.info("Expecting ::/0 to be re-added on r1")
+ routes = {"::/0": {}}
+ expect_ospfv3_routes("r1", routes, wait=30, type="inter-area")
+
+
+def test_nssa_range():
+ """
+ Test NSSA ABR ranges.
+ """
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Configure new addresses on r4 and enable redistribution of connected
+ # routes.
+ config = """
+ configure terminal
+ interface r4-stubnet
+ ipv6 address 2001:db8:1000::1/128
+ ipv6 address 2001:db8:1000::2/128
+ router ospf6
+ redistribute connected
+ """
+ tgen.gears["r4"].vtysh_cmd(config)
+ logger.info("Expecting NSSA-translated external routes to be added on r3")
+ routes = {"2001:db8:1000::1/128": {}, "2001:db8:1000::2/128": {}}
+ expect_ospfv3_routes("r3", routes, wait=30, type="external-2")
+
+ # Configure an NSSA range on r2 (ABR for area 2).
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa range 2001:db8:1000::/64
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+ logger.info("Expecting summarized routes to be removed from r3")
+ for route in ["2001:db8:1000::1/128", "2001:db8:1000::2/128"]:
+ test_func = partial(dont_expect_route, "r3", route, type="external-2")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "{}'s {} summarized route still exists".format("r3", route)
+ assert result is None, assertmsg
+ logger.info("Expecting NSSA range to be added on r3")
+ routes = {
+ "2001:db8:1000::/64": {
+ "metricType": 2,
+ "metricCost": 20,
+ "metricCostE2": 10,
+ }
+ }
+ expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True)
+
+ # Change the NSSA range cost.
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa range 2001:db8:1000::/64 cost 1000
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+ logger.info("Expecting NSSA range to be updated with new cost")
+ routes = {
+ "2001:db8:1000::/64": {
+ "metricType": 2,
+ "metricCost": 20,
+ "metricCostE2": 1000,
+ }
+ }
+ expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True)
+
+ # Configure the NSSA range to not be advertised.
+ config = """
+ configure terminal
+ router ospf6
+ area 2 nssa range 2001:db8:1000::/64 not-advertise
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+ logger.info("Expecting NSSA summary route to be removed")
+ route = "2001:db8:1000::/64"
+ test_func = partial(dont_expect_route, "r3", route, type="external-2")
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = "{}'s {} NSSA summary route still exists".format("r3", route)
+ assert result is None, assertmsg
+
+ # Remove the NSSA range.
+ config = """
+ configure terminal
+ router ospf6
+ no area 2 nssa range 2001:db8:1000::/64
+ """
+ tgen.gears["r2"].vtysh_cmd(config)
+ logger.info("Expecting previously summarized routes to be re-added")
+ routes = {
+ "2001:db8:1000::1/128": {
+ "metricType": 2,
+ "metricCostE2": 20,
+ },
+ "2001:db8:1000::2/128": {
+ "metricType": 2,
+ "metricCostE2": 20,
+ },
+ }
+ expect_ospfv3_routes("r3", routes, wait=30, type="external-2", detail=True)
+
+
+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/ospf_basic_functionality/ospf_asbr_summary_topo1.json b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json
new file mode 100644
index 0000000..1ddccb8
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_topo1.json
@@ -0,0 +1,195 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r0-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR0"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json
new file mode 100644
index 0000000..28e890d
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_asbr_summary_type7_lsa.json
@@ -0,0 +1,199 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_authentication.json b/tests/topotests/ospf_basic_functionality/ospf_authentication.json
new file mode 100644
index 0000000..4edb9f2
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_authentication.json
@@ -0,0 +1,166 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_chaos.json b/tests/topotests/ospf_basic_functionality/ospf_chaos.json
new file mode 100644
index 0000000..ed199f1
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_chaos.json
@@ -0,0 +1,166 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json
new file mode 100644
index 0000000..ea5b9a4
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp.json
@@ -0,0 +1,342 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r1-link1": {
+ "nbr": "r1"
+ },
+ "r1-link2": {
+ "nbr": "r1"
+ },
+ "r1-link3": {
+ "nbr": "r1"
+ },
+ "r1-link4": {
+ "nbr": "r1"
+ },
+ "r1-link5": {
+ "nbr": "r1"
+ },
+ "r1-link6": {
+ "nbr": "r1"
+ },
+ "r1-link7": {
+ "nbr": "r1"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link4": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link5": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link6": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link7": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r0-link1": {
+ "nbr": "r0"
+ },
+ "r0-link2": {
+ "nbr": "r0"
+ },
+ "r0-link3": {
+ "nbr": "r0"
+ },
+ "r0-link4": {
+ "nbr": "r0"
+ },
+ "r0-link5": {
+ "nbr": "r0"
+ },
+ "r0-link6": {
+ "nbr": "r0"
+ },
+ "r0-link7": {
+ "nbr": "r0"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json
new file mode 100644
index 0000000..ecfd35d
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_ecmp_lan.json
@@ -0,0 +1,234 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r4": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r5": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r6": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r7": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.4",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.5",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r6": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.6",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r7": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.7",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_lan.json b/tests/topotests/ospf_basic_functionality/ospf_lan.json
new file mode 100644
index 0000000..126934c
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_lan.json
@@ -0,0 +1,138 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_nssa.json b/tests/topotests/ospf_basic_functionality/ospf_nssa.json
new file mode 100644
index 0000000..f95a297
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_nssa.json
@@ -0,0 +1,188 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "area": [{
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_p2mp.json b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json
new file mode 100644
index 0000000..40815f3
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_p2mp.json
@@ -0,0 +1,198 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-multipoint"
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_routemaps.json b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json
new file mode 100644
index 0000000..60a8434
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_routemaps.json
@@ -0,0 +1,159 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json
new file mode 100644
index 0000000..9062a09
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_rte_calc.json
@@ -0,0 +1,168 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/ospf_single_area.json b/tests/topotests/ospf_basic_functionality/ospf_single_area.json
new file mode 100644
index 0000000..c595912
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/ospf_single_area.json
@@ -0,0 +1,188 @@
+{
+
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "description": "DummyIntftoR1",
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
new file mode 100644
index 0000000..f42bc47
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_topo1.py
@@ -0,0 +1,3313 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ topo_daemons,
+ create_prefix_lists,
+ create_route_maps,
+ create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+NETWORK_11 = {"ipv4": ["11.0.20.6/32", "11.0.20.7/32"]}
+
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ]
+}
+SUMMARY = {"ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"]}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A0 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A0
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A0 +---+
+
+TESTCASES =
+1. OSPF summarisation functionality.
+2. OSPF summarisation with metric type 2.
+3. OSPF summarisation with Tag option
+4. OSPF summarisation with advertise and no advertise option
+5. OSPF summarisation Chaos.
+6. OSPF summarisation with route map filtering.
+7. OSPF summarisation with route map modification of metric type.
+8. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_asbr_summary_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def red_static(dut, config=True):
+ """
+ Local 'def' for Redstribute static routes inside ospf.
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """
+ Local 'def' for Redstribute connected routes inside ospf
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf": {"redistribute": [{"redist_type": "connected", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_type5_summary_tc43_p0(request):
+ """OSPF summarisation with metric type 2."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change the summary address mask to lower match (ex - 16 to 8)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "16"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/16",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Change the summary address mask to higher match (ex - 8 to 24)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "11.0.0.0/16": {
+ "Summary address": "11.0.0.0/24",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+ step("Configure 2 summary address with different mask of same network.")
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(" Un configure one of the summary address.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "24"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "11.0.0.0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc48_p0(request):
+ """OSPF summarisation with route map modification of metric type."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Configure route map and & rule to permit configured summary address,"
+ " redistribute static & connected routes with the route map."
+ )
+ step("Configure prefixlist to permit the static routes, add to route map.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc42_p0(request):
+ """OSPF summarisation functionality."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route. with aggregate timer as 6 sec"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ],
+ "aggr_timer": 6,
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "del_aggr_timer": True,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ dut = "r1"
+ step("All 5 routes are advertised after deletion of configured summary.")
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("configure the summary again and delete static routes .")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ step("Verify that summary route is withdrawn from R1.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Add back static routes.")
+ input_dict_static_rtes = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1."
+ )
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show configure summaries.")
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure new static route which is matching configured summary.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [{"network": NETWORK_11["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete one of the static route.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted static route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step(
+ "Configure redistribute connected and configure ospf external"
+ " summary address to summarise the connected routes."
+ )
+
+ dut = "r0"
+ red_connected(dut)
+ clear_ospf(tgen, dut)
+
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
+
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {"summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured "
+ "summary address on R0 and only one route is sent to R1."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "10.0.0.0/8"}]}}
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Shut one of the interface")
+ intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Un do shut the interface")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete OSPF process.")
+ ospf_del = {"r0": {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Reconfigure ospf process with summary")
+ reset_config_on_routers(tgen)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+ red_connected(dut)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 and only one route is sent to R1."
+ )
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete the redistribute command in ospf.")
+ dut = "r0"
+ red_connected(dut, config=False)
+ red_static(dut, config=False)
+
+ step("Verify that summary route is withdrawn from the peer.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "metric": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc45_p0(request):
+ """OSPF summarisation with Tag option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Repeat steps 1 to 10 of summarisation in non Back bone area.")
+ reset_config_on_routers(tgen)
+
+ step("Change the area id on the interface on R0")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the area id on the interface ")
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv4"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv4"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, tag="88888", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc46_p0(request):
+ """OSPF summarisation with advertise and no advertise option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route with no advertise option."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Summary has 5 sec delay timer, sleep 5 secs...")
+ sleep(5)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Reconfigure summary with no advertise.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Change summary address from no advertise to advertise "
+ "(summary-address 10.0.0.0 255.255.0.0)"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc47_p0(request):
+ """OSPF summarisation with route map filtering."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "configure route map and add rule to permit configured static "
+ "routes, redistribute static & connected routes with the route map."
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the rule from permit to deny in configured route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "deny",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("summary route has 5 secs dealy, sleep 5 secs")
+ sleep(5)
+ step("Verify that advertised summary route is flushed from neighbor.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Reconfigure the route map with denying configure summary address.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": SUMMARY["ipv4"][0], "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that advertised summary route is not flushed from neighbor.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Redistribute static/connected routes without route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Configure rule to deny all the routes in route map and configure"
+ " redistribute command in ospf using route map."
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that no summary route is originated.")
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "seq_id": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure cli in this order - 2 static routes, a route map to "
+ "permit those routes, summary address in ospf to match the "
+ "configured static route network, redistribute the static "
+ "routes with route map"
+ )
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [{"network": NETWORK2["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][1].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ "12.0.0.0/8": {
+ "Summary address": "12.0.0.0/8",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change route map rule for 1 of the routes to deny.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": NETWORK2["ipv4"][0], "action": "deny"},
+ {"seqid": 20, "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that originated type 5 summary lsa is not refreshed because"
+ "of the route map events."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("add rule in route map to deny configured summary address.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "12.0.0.0/8", "action": "deny"},
+ {"seqid": 20, "network": "any", "action": "permit"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that summary route is not denied, summary route should be"
+ " originated if matching prefixes are present."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "12.0.0.0/8"}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc51_p2(request):
+ """OSPF CLI Show.
+
+ verify ospf ASBR summary config and show commands behaviours.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure all the supported OSPF ASBR summary commands on DUT.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure and re configure all the commands 10 times in a loop.")
+
+ for itrate in range(0, 10):
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv4"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the show commands")
+
+ input_dict = {
+ SUMMARY["ipv4"][2]: {
+ "Summary address": SUMMARY["ipv4"][2],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_type5_summary_tc49_p2(request):
+ """OSPF summarisation Chaos."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Kill OSPFd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Bring up OSPFd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("restart zebrad")
+ kill_router_daemons(tgen, "r0", ["zebra"])
+
+ step("Bring up zebra daemon on R0.")
+ start_router_daemons(tgen, "r0", ["zebra"])
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][0]: {
+ "Summary address": SUMMARY["ipv4"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
new file mode 100644
index 0000000..2c9959c
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_asbr_summary_type7_lsa.py
@@ -0,0 +1,409 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ verify_ospf_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ]
+}
+NETWORK3 = {
+ "ipv4": [
+ "13.0.20.1/32",
+ "13.0.20.2/32",
+ "13.0.20.3/32",
+ "13.0.20.4/32",
+ "13.0.20.5/32",
+ ]
+}
+SUMMARY = {"ipv4": ["11.0.20.1/8", "12.0.0.0/8", "13.0.0.0/8", "11.0.0.0/8"]}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+
+
+TESTCASES =
+1. OSPF summarisation with type7 LSAs.
+
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_asbr_summary_type7_lsa.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {"ospf": {"redistribute": [{"redist_type": "static", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_type5_summary_tc44_p0(request):
+ """OSPF summarisation with type7 LSAs"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Bring up the base config as per the topology")
+ step("Configure area 1 as NSSA Area")
+
+ reset_config_on_routers(tgen)
+
+ dut = "r0"
+ protocol = "ospf"
+
+ red_static(dut)
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv4"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][0].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+
+ step(
+ "Configure summary & redistribute static/connected route with " "metric type 2"
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv4"][3]}]}}
+ dut = "r1"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv4"][3]: {
+ "Summary address": SUMMARY["ipv4"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Learn type 7 lsa from neighbours")
+
+ dut = "r1"
+ protocol = "ospf"
+
+ red_static(dut)
+ input_dict_static_rtes = {
+ "r1": {
+ "static_routes": [{"network": NETWORK3["ipv4"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that routes are learnt on R0.")
+ dut = "r0"
+
+ result = verify_ospf_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv4", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that type7 LSAs received from neighbor are not summarised.")
+ input_dict = {
+ "13.0.0.0/8": {
+ "Summary address": "13.0.0.0/8",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that already originated summary is intact.")
+ input_dict = {
+ SUMMARY["ipv4"][3]: {
+ "Summary address": SUMMARY["ipv4"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ dut = "r1"
+ aggr_timer = {"r1": {"ospf": {"aggr_timer": 6}}}
+ result = create_router_ospf(tgen, topo, aggr_timer)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ ospf_summ_r0 = {
+ "r0": {
+ "ospf": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv4"][2].split("/")[0], "mask": "8"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "wait for 6+1 seconds as ospf aggregation start after 6 secs as "
+ "per the above aggr_timer command"
+ )
+ sleep(7)
+ dut = "r1"
+ aggr_timer = {"r1": {"ospf": {"del_aggr_timer": 6}}}
+ result = create_router_ospf(tgen, topo, aggr_timer)
+ 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/ospf_basic_functionality/test_ospf_authentication.py b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
new file mode 100644
index 0000000..2524817
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_authentication.py
@@ -0,0 +1,873 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from time import sleep
+from copy import deepcopy
+from lib.topotest import frr_unicode
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import verify_ospf_neighbor, config_ospf_interface, clear_ospf
+from ipaddress import IPv4Address
+
+pytestmark = [pytest.mark.ospfd]
+
+
+# Global variables
+topo = None
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. Verify ospf authentication with Simple password authentication.
+2. Verify ospf authentication with MD5 authentication.
+3. Verify ospf authentication with different authentication methods.
+
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_authentication.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_authentication_simple_pass_tc28_p1(request):
+ """
+ OSPF Authentication - Verify ospf authentication with Simple
+ password authentication.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf with on R1 and R2, enable ospf on R1 interface"
+ "connected to R2 with simple password authentication using ip ospf "
+ "authentication Simple password cmd."
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("clear ip ospf after configuring the authentication.")
+ clear_ospf(tgen, "r1")
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "On R2 enable ospf on interface with simple password authentication "
+ "using ip ospf authentication Simple password cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable simple password authentication on R2 using no ip ospf "
+ "authentication Simple password cmd."
+ )
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": True,
+ "authentication-key": "ospf",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 neighbour is deleted for R2 after dead interval expiry")
+ # wait till the dead time expiry
+ sleep(6)
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=10
+ )
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with Simple password auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change Ip address on R1 and R2")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"]
+ topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+
+ reset_config_on_routers(tgen, routerName="r1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # clear ip ospf after configuring the authentication.
+ clear_ospf(tgen, "r1")
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 with new "
+ "ip address using show ip ospf "
+ )
+
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_md5_tc29_p1(request):
+ """
+ OSPF Authentication - Verify ospf authentication with MD5 authentication.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf with on R1 and R2, enable ospf on R1 interface "
+ "connected to R2 with message-digest authentication using ip "
+ "ospf authentication message-digest cmd."
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=6
+ )
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "On R2 enable ospf on interface with message-digest authentication"
+ " using ip ospf authentication message-digest password cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Disable message-digest authentication on R2 using no ip ospf "
+ "authentication message-digest password cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=10
+ )
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Again On R2 enable ospf on interface with message-digest auth")
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change Ip address on R1 and R2")
+
+ topo_modify_change_ip = deepcopy(topo)
+
+ intf_ip = topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"]
+
+ topo_modify_change_ip["routers"]["r1"]["links"]["r2"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+
+ reset_config_on_routers(tgen, routerName="r1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ clear_ospf(tgen, "r1")
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 with new "
+ "ip address using show ip ospf "
+ )
+
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_authentication_different_auths_tc30_p1(request):
+ """
+ OSPF Authentication - Verify ospf authentication with different
+ authentication methods.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf with on R1 and R2, enable ospf on R1 interface "
+ "connected to R2 with message-digest authentication using ip "
+ "ospf authentication message-digest cmd."
+ )
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # wait for dead timer expiry
+ sleep(6)
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=10
+ )
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "On R2 enable ospf on interface with message-digest authentication"
+ " using ip ospf authentication message-digest password cmd."
+ )
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Delete the configured password on both the routers.")
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the deletion is successful and neighbour is FULL"
+ " between R1 and R2 using show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change the authentication type to simple password.")
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {"ospf": {"authentication": True, "authentication-key": "ospf"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the deletion is successful and neighbour is"
+ " FULL between R1 and R2 using show ip "
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change the password in simple password.")
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {"ospf": {"authentication": True, "authentication-key": "OSPFv4"}}
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the deletion is successful and neighbour is"
+ " FULL between R1 and R2 using show ip "
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Delete the password authentication on the interface ")
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": True,
+ "authentication-key": "OSPFv4",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": True,
+ "authentication-key": "OSPFv4",
+ "del_action": True,
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the deletion is successful and neighbour is"
+ " FULL between R1 and R2 using show ip "
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Enable Md5 authentication on the interface")
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "ospf",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change the MD5 authentication password")
+
+ r1_ospf_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "OSPFv4",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ r2_ospf_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf": {
+ "authentication": "message-digest",
+ "authentication-key": "OSPFv4",
+ "message-digest-key": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf_interface(tgen, topo, r2_ospf_auth)
+ 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/ospf_basic_functionality/test_ospf_chaos.py b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
new file mode 100644
index 0000000..a0ab828
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_chaos.py
@@ -0,0 +1,530 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ topo_daemons,
+ verify_rib,
+ stop_router,
+ start_router,
+ create_static_routes,
+ start_router_daemons,
+ kill_router_daemons,
+)
+
+from lib.ospf import verify_ospf_neighbor, verify_ospf_rib, create_router_ospf
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+"""
+Topology:
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. Verify ospf functionality after restart ospfd.
+2. Verify ospf functionality after restart FRR service.
+3. Verify ospf functionality when staticd is restarted.
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_chaos.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+def test_ospf_chaos_tc31_p1(request):
+ """Verify ospf functionality after restart ospfd."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Kill OSPFd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are down after killing ospfd in R0")
+ dut = "r0"
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route advertised to R1 are deleted from RIB and FIB.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Bring up OSPFd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Kill OSPFd daemon on R1.")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Verify OSPF neighbors are down after killing ospfd in R1")
+ dut = "r1"
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf_covergence is not True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Bring up OSPFd daemon on R1.")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_chaos_tc32_p1(request):
+ """Verify ospf functionality after restart FRR service."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Restart frr on R0")
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step("Verify OSPF neighbors are up after restarting R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Restart frr on R1")
+ stop_router(tgen, "r1")
+ start_router(tgen, "r1")
+
+ step("Verify OSPF neighbors are up after restarting R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_chaos_tc34_p1(request):
+ """
+ verify ospf functionality when staticd is restarted.
+
+ Verify ospf functionalitywhen staticroutes are
+ redistributed & Staticd is restarted.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {"r0": {"ospf": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify OSPF neighbors after base config is done.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Kill staticd daemon on R0.")
+ kill_router_daemons(tgen, "r0", ["staticd"])
+
+ step("Verify that route advertised to R1 are deleted from RIB and FIB.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Bring up staticd daemon on R0.")
+ start_router_daemons(tgen, "r0", ["staticd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R0")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Kill staticd daemon on R1.")
+ kill_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Bring up staticd daemon on R1.")
+ start_router_daemons(tgen, "r1", ["staticd"])
+
+ step("Verify OSPF neighbors are up after bringing back ospfd in R1")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "All the neighbours are up and routes are installed before the"
+ " restart. Verify OSPF route table and ip route table."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ 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/ospf_basic_functionality/test_ospf_ecmp.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
new file mode 100644
index 0000000..2b479db
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp.py
@@ -0,0 +1,457 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+)
+from lib.topolog import logger
+
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ config_ospf_interface,
+ verify_ospf_rib,
+ redistribute_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+topo = None
+
+
+# Global variables
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+"""
+TOPOLOGY :
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES :
+1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level)
+2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_ecmp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_ecmp_tc16_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8 (ECMP
+ configured at FRR level)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.")
+ reset_config_on_routers(tgen)
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ step("Verify that route in R2 in stalled with 8 next hops.")
+ nh = []
+ for item in range(1, 7):
+ nh.append(topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0])
+
+ nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+
+ nh.append(nh2)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut all the interfaces on the remote router - R2")
+ dut = "r1"
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut on all the interfaces on DUT (r1)")
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that all the neighbours are up and routes are installed"
+ " with 8 next hop in ospf and ip route tables on R1."
+ )
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure static route on R0")
+
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
+
+ # Wait for R0 to flush external LSAs.
+ sleep(10)
+
+ step("Verify that route is withdrawn from R2.")
+ dut = "r1"
+ result = verify_ospf_rib(
+ tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ retry_timeout=10,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Re configure the static route in R0.")
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_ecmp_tc17_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.")
+ reset_config_on_routers(tgen)
+ step("Verify that OSPF is up with 2 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ step("Verify that route in R2 in stalled with 2 next hops.")
+
+ nh1 = topo["routers"]["r0"]["links"]["r1-link1"]["ipv4"].split("/")[0]
+ nh2 = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ nh = [nh1, nh2]
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure static route on R0")
+
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static", delete=True)
+ # sleep till the route gets withdrawn
+ sleep(10)
+
+ step("Verify that route is withdrawn from R2.")
+ dut = "r1"
+ result = verify_ospf_rib(
+ tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ retry_timeout=10,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reconfigure the static route in R0.Change ECMP value to 2.")
+ dut = "r0"
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ step("Configure cost on R0 as 100")
+ r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 100}}}}}
+ result = config_ospf_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ 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/ospf_basic_functionality/test_ospf_ecmp_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
new file mode 100644
index 0000000..00feefc
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_ecmp_lan.py
@@ -0,0 +1,307 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ redistribute_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["1::1/128", "1::2/128", "1::3/128", "1::4/128", "1::5/128"],
+}
+MASK = {"ipv4": "32", "ipv6": "128"}
+NEXT_HOP = {
+ "ipv4": ["10.0.0.1", "10.0.1.1", "10.0.2.1", "10.0.3.1", "10.0.4.1"],
+ "ipv6": ["Null0", "Null0", "Null0", "Null0", "Null0"],
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+TESTCASES =
+1. Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_ecmp_lan.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_lan_ecmp_tc18_p0(request):
+ """
+ OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step(". Configure ospf in all the routers on LAN interface.")
+ reset_config_on_routers(tgen)
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Configure a static route in all the routes and "
+ "redistribute static/connected in OSPF."
+ )
+
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = rtr
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ step(
+ "Verify that route in R0 in stalled with 8 hops. "
+ "Verify ospf route table and ip route table."
+ )
+
+ nh = []
+ for rtr in topo["routers"]:
+ nh.append(topo["routers"][rtr]["links"]["s1"]["ipv4"].split("/")[0])
+ nh.remove(topo["routers"]["r1"]["links"]["s1"]["ipv4"].split("/")[0])
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" clear ip ospf interface on DUT(r0)")
+ clear_ospf(tgen, "r0")
+
+ step(
+ "Verify that after clearing the ospf interface all the "
+ "neighbours are up and routes are installed with 8 next hop "
+ "in ospf and ip route tables on R0"
+ )
+
+ dut = "r0"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" clear ip ospf interface on R2")
+ clear_ospf(tgen, "r2")
+
+ dut = "r2"
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Delete static/connected cmd in ospf in all the routes one by one.")
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that all the routes are withdrawn from R0")
+ dut = "r1"
+ result = verify_ospf_rib(
+ tgen, dut, input_dict, next_hop=nh, retry_timeout=10, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ retry_timeout=10,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \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/ospf_basic_functionality/test_ospf_lan.py b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
new file mode 100644
index 0000000..497a8b9
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_lan.py
@@ -0,0 +1,665 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+from lib.topotest import frr_unicode
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ create_interfaces_cfg,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ create_router_ospf,
+ verify_ospf_interface,
+)
+from ipaddress import IPv4Address
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+1. OSPF Hello protocol - Verify DR BDR Elections
+2. OSPF IFSM -Verify state change events on DR / BDR / DR Other
+
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_lan.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_lan_tc1_p0(request):
+ """
+ OSPF Hello protocol - Verify DR BDR Elections
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step("Verify that DR BDR DRother are elected in the LAN.")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "DR"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that all the routers are in FULL state with DR and BDR "
+ "in the topology"
+ )
+
+ input_dict = {
+ "r1": {
+ "ospf": {
+ "neighbors": {
+ "r0": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r1"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"priority": 100},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in ["r0", "r1", "r2", "r3"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DR")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure DR pririty 150 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"priority": 150},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in ["r0", "r1"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DR")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure DR priority 0 on R0 & Clear ospf nbrs on all the routers")
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"priority": 0},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in ["r1"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DRother")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "DR"},
+ "r2": {"state": "2-Way", "role": "DROther"},
+ "r3": {"state": "2-Way", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure DR priority to default on R0 and Clear ospf neighbors"
+ " on all the routers"
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"priority": 100},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in ["r0", "r1"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DR")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["s1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ result = verify_ospf_neighbor(tgen, topo, dut, lan=True, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r0: OSPF neighbors-hip is up \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("No Shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["s1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "DR"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ step("Verify that after no shut ospf neighbours are full on R0.")
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf on DR router in the topology.")
+ clear_ospf(tgen, "r0")
+
+ step("Verify that BDR is getting promoted to DR after clear.")
+ step("Verify that all the nbrs are in FULL state with the elected DR.")
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on LAN intf on R0 to other ip from the same subnet.")
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 4
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+
+ clear_ospf(tgen, "r0")
+ step(
+ "Verify that OSPF is in FULL state with other routers with "
+ "newly configured IP."
+ )
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ospf router id on the R0 and clear ip ospf interface.")
+ change_rid = {"r0": {"ospf": {"router_id": "100.1.1.100"}}}
+
+ result = create_router_ospf(tgen, topo, change_rid)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.100"
+ step("Reload the FRR router")
+
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step(
+ "Verify that OSPF is in FULL state with other routers with"
+ " newly configured router id."
+ )
+ input_dict = {
+ "r1": {
+ "ospf": {
+ "neighbors": {
+ "r0": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r1"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Reconfigure the original router id and clear ip ospf interface.")
+ change_rid = {"r0": {"ospf": {"router_id": "100.1.1.0"}}}
+ result = create_router_ospf(tgen, topo, change_rid)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ topo["routers"]["r0"]["ospf"]["router_id"] = "100.1.1.0"
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step("Verify that OSPF is enabled with router id previously configured.")
+ input_dict = {
+ "r1": {
+ "ospf": {
+ "neighbors": {
+ "r0": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r1"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_lan_tc2_p0(request):
+ """
+ OSPF IFSM -Verify state change events on DR / BDR / DR Other
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "ospf": {
+ "priority": 98,
+ "timerDeadSecs": 4,
+ "area": "0.0.0.3",
+ "mcastMemberOspfDesignatedRouters": True,
+ "mcastMemberOspfAllRouters": True,
+ "ospfEnabled": True,
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["s1"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "s1"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["s1"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["s1"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "s1"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["s1"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"area": "0.0.0.3"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "s1": {
+ "interface": topo["routers"]["r0"]["links"]["s1"]["interface"],
+ "ospf": {"area": "0.0.0.2"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {"links": {"s1": {"ospf": {"area": "0.0.0.2", "ospfEnabled": True}}}}
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ 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/ospf_basic_functionality/test_ospf_nssa.py b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
new file mode 100644
index 0000000..1917bd4
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_nssa.py
@@ -0,0 +1,279 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import ipaddress
+from lib.ospf import (
+ verify_ospf_neighbor,
+ verify_ospf_rib,
+ create_router_ospf,
+ redistribute_ospf,
+)
+from lib.topojson import build_config_from_json
+from lib.topolog import logger
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ topo_daemons,
+)
+from lib.topogen import Topogen, get_topogen
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+
+
+TESTCASES =
+1. OSPF Learning - Verify OSPF can learn different types of LSA and
+ processes them.[Edge learning different types of LSAs]
+2. Verify that ospf non back bone area can be configured as NSSA area
+3. Verify that ospf NSSA area DUT is capable receiving & processing
+ Type7 N2 route.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_nssa.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_learning_tc15_p0(request):
+ """Verify OSPF can learn different types of LSA and processes them.
+
+ OSPF Learning : Edge learning different types of LSAs.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure area 1 as NSSA Area")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that Type 3 summary LSA is originated for the same Area 0")
+ ip = topo["routers"]["r1"]["links"]["r3-link0"]["ipv4"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+ dut = "r0"
+ input_dict = {
+ "r1": {
+ "static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N IA"}]
+ }
+ }
+
+ dut = "r0"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static route in R2 ospf.")
+ dut = "r2"
+ redistribute_ospf(tgen, topo, dut, "static")
+
+ step("Verify that Type 5 LSA is originated by R2.")
+ dut = "r0"
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that R0 receives Type 4 summary LSA.")
+ dut = "r0"
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Change area 1 as non nssa area (on the fly changing area" " type on DUT).")
+
+ for rtr in ["r1", "r2", "r3"]:
+ input_dict = {
+ rtr: {"ospf": {"area": [{"id": "0.0.0.2", "type": "nssa", "delete": True}]}}
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify that OSPF neighbours are reset after changing area type.")
+ step("Verify that ABR R2 originates type 5 LSA in area 1.")
+ step("Verify that route is calculated and installed in R1.")
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "routeType": "N E2"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ 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/ospf_basic_functionality/test_ospf_p2mp.py b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
new file mode 100644
index 0000000..e131fba
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_p2mp.py
@@ -0,0 +1,537 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+from ipaddress import IPv4Address
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ retry,
+ run_frr_cmd,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.topotest import frr_unicode, json_cmp
+
+from lib.ospf import (
+ verify_ospf_interface,
+)
+
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF P2MP -Verify state change events on p2mp network.
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_p2mp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_p2mp_tc1_p0(request):
+ """OSPF IFSM -Verify state change events on p2mp network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}}
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ],
+ "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "interface"
+ ],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}}
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify if interface is enabled with network type P2MP")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0", "networkType": "POINTOMULTIPOINT"},
+ }
+ }
+ }
+ }
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+@retry(retry_timeout=30)
+def verify_ospf_json(tgen, dut, input_dict, cmd="show ip ospf database json"):
+ del tgen
+ show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
+ if not bool(show_ospf_json):
+ return "ospf is not running"
+ result = json_cmp(show_ospf_json, input_dict)
+ return str(result) if result else None
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_nbrs(tgen):
+ db_full = {
+ "areas": {
+ "0.0.0.0": {
+ "routerLinkStates": [
+ {
+ "lsId": "100.1.1.0",
+ "advertisedRouter": "100.1.1.0",
+ "numOfRouterLinks": 6,
+ },
+ {
+ "lsId": "100.1.1.1",
+ "advertisedRouter": "100.1.1.1",
+ "numOfRouterLinks": 6,
+ },
+ {
+ "lsId": "100.1.1.2",
+ "advertisedRouter": "100.1.1.2",
+ "numOfRouterLinks": 6,
+ },
+ {
+ "lsId": "100.1.1.3",
+ "advertisedRouter": "100.1.1.3",
+ "numOfRouterLinks": 7,
+ },
+ ]
+ }
+ }
+ }
+ input = [
+ [
+ "r0",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.1": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r1",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r2",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.1": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.3": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ [
+ "r3",
+ "show ip ospf n json",
+ {
+ "neighbors": {
+ "100.1.1.0": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.1": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ "100.1.1.2": [
+ {
+ "state": "Full/DROther",
+ }
+ ],
+ }
+ },
+ ],
+ ["r0", "show ip ospf database json", db_full],
+ ["r1", "show ip ospf database json", db_full],
+ ["r2", "show ip ospf database json", db_full],
+ ["r3", "show ip ospf database json", db_full],
+ ["r0", "show ip ospf database json", db_full],
+ ["r0", "show ip ospf database router json", {}],
+ ["r0", "show ip ospf interface traffic json", {}],
+ ["r1", "show ip ospf interface traffic json", {}],
+ ["r2", "show ip ospf interface traffic json", {}],
+ ["r3", "show ip ospf interface traffic json", {}],
+ ]
+ for cmd_set in input:
+ step("test_ospf: %s - %s" % (cmd_set[0], cmd_set[1]))
+ assert (
+ verify_ospf_json(tgen, tgen.gears[cmd_set[0]], cmd_set[2], cmd_set[1])
+ is None
+ )
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
new file mode 100644
index 0000000..22d768d
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_routemaps.py
@@ -0,0 +1,1315 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_prefix_lists,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ verify_prefix_lists,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ verify_ospf_rib,
+ create_router_ospf,
+ redistribute_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"]
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF Route map - Verify OSPF route map support functionality.
+2. Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+3. Verify OSPF route map support functionality with set/match clauses
+ /call/continue/goto in a route-map to see if it takes immediate effect.
+4. Verify OSPF route map support functionality
+ when route map actions are toggled.
+5. Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+6. Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+7. Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_routemaps.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_routemaps_functionality_tc19_p0(request):
+ """
+ OSPF Route map - Verify OSPF route map support functionality.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ redistribute_ospf(tgen, topo, "r0", "static")
+
+ dut = "r1"
+ lsid = NETWORK["ipv4"][0].split("/")[0]
+ rid = routerids[0]
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ redistribute_ospf(tgen, topo, "r0", "static", delete=True)
+
+ step(
+ "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv4"][0],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure route map rmap1 and redistribute static routes to"
+ " ospf using route map rmap1"
+ )
+
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+
+ step("Change prefix rules to permit 10.0.20.2 and deny 10.0.20.1")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv4"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are present in fib \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete and reconfigure prefix list.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv4"][1],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": 11,
+ "network": "any",
+ "action": "deny",
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv4"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv4"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_routemaps_functionality_tc20_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute to ospf using route map ( non existent route map)")
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+
+ step(
+ "Verify that routes are not allowed in OSPF even tough no "
+ "matching routing map is configured."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, retry_timeout=4, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ retry_timeout=4,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "configure the route map with the same name that is used "
+ "in the ospf with deny rule."
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete the route map.")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv4": [{"action": "deny", "delete": True}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are allowed in OSPF even tough "
+ "no matching routing map is configured."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: OSPF routes are present \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n " "r1: routes are still present \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_routemaps_functionality_tc21_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality with set/match clauses
+ /call/continue/goto in a route-map to see if it takes immediate effect.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv4": [{"action": "permit", "seq_id": 10}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route is advertised to R2.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [{"action": "permit", "delete": True, "seq_id": 10}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Configure route map with set clause (set metric)")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [{"action": "permit", "set": {"med": 123}, "seq_id": 10}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that configured metric is applied to ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure route map with match clause (match metric) with "
+ "some actions(change metric)."
+ )
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"med": 123},
+ "set": {"med": 150},
+ "seq_id": 10,
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure route map with call clause")
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 150},
+ "call": "rmap_match_pf_2_ipv4",
+ "seq_id": 10,
+ }
+ ],
+ "rmap_match_pf_2_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 200},
+ "seq_id": 10,
+ }
+ ],
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"delete": True}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure route map with continue clause")
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 150},
+ "continue": "30",
+ "seq_id": 10,
+ },
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 100},
+ "seq_id": 20,
+ },
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 50},
+ "seq_id": 30,
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure route map with goto clause")
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "goto": "30",
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 100},
+ },
+ {
+ "action": "permit",
+ "seq_id": "30",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ "set": {"med": 200},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_routemaps_functionality_tc24_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ redistribute_ospf(tgen, topo, "r0", "static", route_map="rmap_ipv4")
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "tag": 1000,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [{"action": "permit", "match": {"ipv4": {"tag": "1000"}}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with tag in route map")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"tag": "1000", "delete": True}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with metric in route map.")
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_routemaps_functionality_tc25_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality
+ when route map actions are toggled.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with permit rule")
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv4": [{"action": "permit"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with deny rule")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv4": [{"seq_id": 10, "action": "deny"}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is not advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_routemaps_functionality_tc22_p0(request):
+ """
+ OSPF Route map - Multiple sequence numbers.
+
+ Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure route map with seq number 10 to with ip prefix"
+ " permitting route 10.0.20.1/32 in R1"
+ )
+ step(
+ "Configure route map with seq number 20 to with ip prefix"
+ " permitting route 10.0.20.2/32 in R1"
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv4": {"prefix_lists": "pf_list_1_ipv4"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv4": {"prefix_lists": "pf_list_2_ipv4"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_1_ipv4": [
+ {"seqid": 10, "network": NETWORK["ipv4"][0], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv4": {
+ "pf_list_2_ipv4": [
+ {"seqid": 10, "network": NETWORK["ipv4"][1], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure redistribute static route with route map.")
+ ospf_red_r0 = {
+ "r0": {
+ "ospf": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv4"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 2,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that both routes are learned in R1 and R2")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change route map with seq number 20 to deny.")
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv4": [
+ {
+ "action": "deny",
+ "seq_id": "20",
+ "match": {"ipv4": {"prefix_lists": "pf_list_2_ipv4"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify the route 10.0.20.2/32 is withdrawn and not present "
+ "in the routing table of R0 and R1."
+ )
+
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv4"][1], "next_hop": "Null0"}]}
+ }
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv4", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result is not 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/ospf_basic_functionality/test_ospf_rte_calc.py b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
new file mode 100644
index 0000000..8bd81a3
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_rte_calc.py
@@ -0,0 +1,795 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import ipaddress
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ create_interfaces_cfg,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+)
+from lib.bgp import verify_bgp_convergence, create_router_bgp
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_rib,
+ redistribute_ospf,
+ config_ospf_interface,
+ verify_ospf_interface,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+# number of retries.
+nretry = 5
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ]
+}
+
+NETWORK_APP_E = {"ipv4": ["12.0.0.0/24", "12.0.0.0/16", "12.0.0.0/8"]}
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+"""
+
+TESTCASES = """
+1. Test OSPF intra area route calculations.
+2. Test OSPF inter area route calculations.
+3. Test OSPF redistribution of connected routes.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_rte_calc.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_redistribution_tc5_p0(request):
+ """Test OSPF intra area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ for num in range(0, nretry):
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ if result is not True:
+ break
+
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: OSPF routes are present after deleting ip address of newly "
+ "configured interface of R0 \n Error: {}".format(tc_name, result)
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ retry_timeout=10,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: OSPF routes are present in fib after deleting ip address of newly "
+ "configured interface of R0 \n Error: {}".format(tc_name, result)
+ )
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_redistribution_tc6_p0(request):
+ """Test OSPF inter area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ for num in range(0, nretry):
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ if result is not True:
+ break
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: OSPF routes are present after deleting ip address of newly "
+ "configured loopback of R0 \n Error: {}".format(tc_name, result)
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen,
+ "ipv4",
+ dut,
+ input_dict,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \n "
+ "r1: OSPF routes are present in fib after deleting ip address of newly "
+ "configured loopback of R0 \n Error: {}".format(tc_name, result)
+ )
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_redistribution_tc8_p1(request):
+ """
+ Test OSPF redistribution of connected routes.
+
+ Verify OSPF redistribution of connected routes when bgp multi hop
+ neighbor is configured using ospf routes
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ step(
+ "Configure loopback interface on all routers, and redistribut"
+ "e connected routes into ospf"
+ )
+ reset_config_on_routers(tgen)
+
+ step(
+ "verify that connected routes -loopback is found in all routers"
+ "advertised/exchaged via ospf"
+ )
+ for rtr in topo["routers"]:
+ redistribute_ospf(tgen, topo, rtr, "static")
+ redistribute_ospf(tgen, topo, rtr, "connected")
+ for node in topo["routers"]:
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": topo["routers"][node]["links"]["lo"]["ipv4"],
+ "no_of_ip": 1,
+ }
+ ]
+ }
+ }
+ for rtr in topo["routers"]:
+ result = verify_rib(tgen, "ipv4", rtr, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure E BGP multi hop using the loopback addresses.")
+ as_num = 100
+ for node in topo["routers"]:
+ as_num += 1
+ topo["routers"][node].update(
+ {
+ "bgp": {
+ "local_as": as_num,
+ "address_family": {"ipv4": {"unicast": {"neighbor": {}}}},
+ }
+ }
+ )
+ for node in topo["routers"]:
+ for rtr in topo["routers"]:
+ if node is not rtr:
+ topo["routers"][node]["bgp"]["address_family"]["ipv4"]["unicast"][
+ "neighbor"
+ ].update(
+ {
+ rtr: {
+ "dest_link": {
+ "lo": {"source_link": "lo", "ebgp_multihop": 2}
+ }
+ }
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, topo["routers"])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that BGP neighbor is ESTABLISHED")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Configure couple of static routes in R0 and "
+ "Redistribute static routes in R1 bgp."
+ )
+
+ for rtr in topo["routers"]:
+ redistribute_ospf(tgen, topo, rtr, "static", delete=True)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv4"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r0 = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv4": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv4", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clear ospf neighbours in R0")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that OSPF neighbours are reset and forms new adjacencies.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that BGP neighbours are reset and forms new adjacencies.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv4", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_rfc2328_appendinxE_p0(request):
+ """
+ Test OSPF appendinx E RFC2328.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbours are Full.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ redistribute_ospf(tgen, topo, "r0", "static")
+
+ step("Configure static route with prefix 24, 16, 8 to check ")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][2],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that ospf originates routes with mask 24, 16, 8")
+ ip_net = NETWORK_APP_E["ipv4"][0]
+ input_dict = {"r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1}]}}
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ip_net = NETWORK_APP_E["ipv4"][1]
+ input_dict = {"r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1}]}}
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ip_net = NETWORK_APP_E["ipv4"][2]
+ input_dict = {"r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1}]}}
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv4", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete static route with prefix 24, 16, 8 to check ")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK_APP_E["ipv4"][2],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_cost_tc52_p0(request):
+ """OSPF Cost - verifying ospf interface cost functionality"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure ospf cost as 20 on interface between R0 and R1. "
+ "Configure ospf cost as 30 between interface between R0 and R2."
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf": {"cost": 20}}, "r2": {"ospf": {"cost": 30}}}}
+ }
+ result = config_ospf_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between"
+ " r0 and r1 as 30 and r0 and r2 as 20"
+ )
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Swap the costs between interfaces on r0, between r0 and r1 to 30"
+ ", r0 and r2 to 20"
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf": {"cost": 30}}, "r2": {"ospf": {"cost": 20}}}}
+ }
+ result = config_ospf_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0 "
+ "and r1 as 30 and r0 and r2 as 20."
+ )
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r1.")
+
+ r0_ospf_cost = {"r0": {"links": {"r1": {"ospf": {"cost": 30, "del_action": True}}}}}
+ result = config_ospf_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf": {"cost": 10}}, "r2": {"ospf": {"cost": 20}}}}
+ }
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ " and r1 as 10 and r0 and r2 as 20."
+ )
+
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r2.")
+
+ r0_ospf_cost = {"r0": {"links": {"r2": {"ospf": {"cost": 20, "del_action": True}}}}}
+ result = config_ospf_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ "and r1 as 10 and r0 and r2 as 10"
+ )
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf": {"cost": 10}}, "r2": {"ospf": {"cost": 10}}}}
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ 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/ospf_basic_functionality/test_ospf_single_area.py b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
new file mode 100644
index 0000000..21a7d83
--- /dev/null
+++ b/tests/topotests/ospf_basic_functionality/test_ospf_single_area.py
@@ -0,0 +1,1015 @@
+#!/usr/bin/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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf_rib,
+ verify_ospf_interface,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+# Global variables
+topo = None
+
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF IFSM -Verify state change events on p2p network.
+2. OSPF Timers - Verify OSPF interface timer hello interval functionality
+3. OSPF Timers - Verify OSPF interface timer dead interval functionality
+4. Verify ospf show commands with json output.
+5. Verify NFSM events when ospf nbr changes with different MTU values.
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospf_single_area.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_p2p_tc3_p0(request):
+ """OSPF IFSM -Verify state change events on p2p network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {"ospf": {"mcastMemberOspfAllRouters": True, "ospfEnabled": True}}
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo["routers"]["r0"]["links"]["r3"]["ipv4"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv4"] = str(
+ IPv4Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf": {
+ "ipAddress": topo_modify_change_ip["routers"]["r0"]["links"][
+ "r3"
+ ]["ipv4"].split("/")[0],
+ "ipAddressPrefixlen": int(
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ].split("/")[1]
+ ),
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv4": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv4"
+ ],
+ "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "interface"
+ ],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {"links": {"r3": {"ospf": {"area": "0.0.0.1", "ospfEnabled": True}}}}
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.1"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether BGP is converged
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_hello_tc10_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer hello interval functionality
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("modify hello timer from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {"r0": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}}
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify hello timer from default value to r1 hello timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {"r1": {"ospf": {"timerMsecs": 11 * 1000, "timerDeadSecs": 12}}}
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}}
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {"r1": {"ospf": {"timerMsecs": 10 * 1000, "timerDeadSecs": 40}}}
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("configure hello timer = 1 on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf": {"timerMsecs": 1 * 1000, "timerDeadSecs": 4}}}}
+ }
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_show_p1(request):
+ """Verify ospf show commands with json output."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "ospf": {
+ "ifUp": True,
+ "ifFlags": "<UP,BROADCAST,RUNNING,MULTICAST>",
+ "ospfEnabled": True,
+ "ipAddressPrefixlen": 24,
+ "ospfIfType": "Broadcast",
+ "area": "0.0.0.0",
+ "networkType": "BROADCAST",
+ "cost": 10,
+ "transmitDelaySecs": 1,
+ "state": "DR",
+ "priority": 1,
+ "mcastMemberOspfAllRouters": True,
+ "timerMsecs": 1000,
+ "timerDeadSecs": 4,
+ "timerWaitSecs": 4,
+ "timerRetransmitSecs": 5,
+ "nbrCount": 1,
+ "nbrAdjacentCount": 1,
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # show ip ospf route
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv4"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv4"].split("/")[0]
+ input_dict = {
+ "r1": {"static_routes": [{"network": ip_net, "no_of_ip": 1, "routeType": "N"}]}
+ }
+
+ dut = "r1"
+ result = verify_ospf_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_dead_tc11_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer dead interval functionality
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("modify dead interval from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"hello_interval": 12, "dead_interval": 48},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 48}}}}}
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify dead interval from default value to r1" "dead interval timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"dead_interval": 48, "hello_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 48}}}}}
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 40}}}}}
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Configure dead timer = 65535 on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"timerDeadSecs": 65535}}}}}
+ dut = "r0"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Try configuring timer values outside range for example 65536")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf": {"dead_interval": 65536},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Unconfigure the dead timer from the interface from r1 and r2.")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf": {"dead_interval": 65535},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that timer value is deleted from intf & " "set to default value 40 sec."
+ )
+ input_dict = {"r1": {"links": {"r0": {"ospf": {"timerDeadSecs": 40}}}}}
+ dut = "r1"
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_tc4_mtu_ignore_p0(request):
+ """
+ OSPF NFSM - MTU change
+
+ Verify NFSM events when ospf nbr changes with different MTU values
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step(" Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ step("Verify that OSPF neighbors are FULL.")
+ reset_config_on_routers(tgen)
+ result = verify_ospf_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ rtr0 = tgen.routers()["r0"]
+ rtr1 = tgen.routers()["r1"]
+
+ r0_r1_intf = topo["routers"]["r0"]["links"]["r1"]["interface"]
+ r1_r0_intf = topo["routers"]["r1"]["links"]["r0"]["interface"]
+
+ rtr0.run("ip link set {} mtu 1200".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step(
+ "Verify that configured MTU value is updated in the show ip " "ospf interface."
+ )
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 1200}}}}}
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+ rtr0.run("ip link set {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure mtu ignore and change the value of the mtu to non default"
+ " on R0 to R1 interface. Reset ospf neighbors on R0."
+ )
+ r0_ospf_mtu = {"r0": {"links": {"r1": {"ospf": {"mtu_ignore": True}}}}}
+ result = config_ospf_interface(tgen, topo, r0_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuMismatchDetect": True}}}}}
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ r1_ospf_mtu = {"r1": {"links": {"r0": {"ospf": {"mtu_ignore": True}}}}}
+ result = config_ospf_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ rtr0.run("ip link set {} mtu 1200".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Unconfigure mtu-ignore command from the interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ r1_ospf_mtu = {
+ "r1": {"links": {"r0": {"ospf": {"mtu_ignore": True, "del_action": True}}}}
+ }
+ result = config_ospf_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ clear_ospf(tgen, "r0")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step("Modify the MTU to again default valaue on R0 to R1 interface.")
+
+ rtr0.run("ip link set {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
+ )
+
+ rtr0.run("ip link set {} mtu 9216".format(r0_r1_intf))
+ rtr1.run("ip link set {} mtu 9216".format(r1_r0_intf))
+
+ clear_ospf(tgen, "r0")
+ clear_ospf(tgen, "r1")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that jumbo MTU is updated in the show ip ospf interface.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf": {"mtuBytes": 9216}}}}}
+ result = verify_ospf_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ 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/ospf_dual_stack/test_ospf_dual_stack.dot b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.dot
new file mode 100644
index 0000000..2c6d0aa
--- /dev/null
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.dot
@@ -0,0 +1,107 @@
+## 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 ospf_topo1 {
+ label="ospf dual stack";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 1.1.1.1/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ label="r2\nrtr-id 2.2.2.2/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ label="r3\nrtr-id 3.3.3.3/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ label="r4\nrtr-id 4.4.4.4/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r5 [
+ label="r5\nrtr-id 5.5.5.5/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ label="s1\n10.0.13.0/24\n2013:13::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.23.0/24\n2023:23::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ label="s3\n10.0.34.0/24\n2034:34::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ label="s4\n10.0.24.0/24\n2024:24::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ label="s5\n10.0.45.0/24\n2045:45::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster1 {
+ label="area 1.1.1.1"
+
+ r1 -- s1 [label="eth0\n.1\n::1"];
+ r3 -- s1 [label="eth0\n.3\n::3"];
+ r3 -- s2 [label="eth1\n.3\n::3"];
+ r2 -- s2 [label="eth0\n.2\n::2"];
+ }
+
+ subgraph cluster0 {
+ label="area 0.0.0.0"
+
+ r3 -- s3 [label="eth2\n.3\n::3"];
+ r4 -- s3 [label="eth0\n.4\n::4"];
+ r2 -- s4 [label="eth1\n.2\n::2"];
+ r4 -- s4 [label="eth1\n.4\n::4"];
+ }
+
+ subgraph cluster2 {
+ label="area 2.2.2.2"
+
+ r4 -- s5 [label="eth2\n.4\n::4"];
+ r5 -- s5 [label="eth0\n.5\n::5"];
+ }
+}
diff --git a/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.jpg b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.jpg
new file mode 100644
index 0000000..44efda8
--- /dev/null
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.jpg
Binary files differ
diff --git a/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.json b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.json
new file mode 100644
index 0000000..cdb8813
--- /dev/null
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.json
@@ -0,0 +1,263 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "r3": {
+ "ipv4": "10.0.13.1/24",
+ "ipv6": "2013:13::1/64",
+ "ospf": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "area": "1.1.1.1"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r3": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r3": {
+ "ipv4": "10.0.23.2/24",
+ "ipv6": "2023:23::2/64",
+ "ospf": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "1.1.1.1"
+ }
+ },
+ "r4": {
+ "ipv4": "10.0.24.2/24",
+ "ipv6": "2034:34::2/64",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "2.2.2.2",
+ "neighbors": {
+ "r3": {},
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "2.2.2.2",
+ "neighbors": {
+ "r3": {},
+ "r4": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r1": {
+ "ipv4": "10.0.13.3/24",
+ "ipv6": "2013:13::3/64",
+ "ospf": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "1.1.1.1"
+ }
+ },
+ "r2": {
+ "ipv4": "10.0.23.3/24",
+ "ipv6": "2023:23::3/64",
+ "ospf": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "1.1.1.1"
+ }
+ },
+ "r4": {
+ "ipv4": "10.0.34.3/24",
+ "ipv6": "2034:34::3/64",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "3.3.3.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "3.3.3.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r4": {}
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "r2": {
+ "ipv4": "10.0.24.4/24",
+ "ipv6": "2024:24::4/64",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "0.0.0.0"
+ }
+ },
+ "r3": {
+ "ipv4": "10.0.34.4/24",
+ "ipv6": "2034:34::4/64",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "0.0.0.0"
+ }
+ },
+ "r5": {
+ "ipv4": "10.0.45.4/24",
+ "ipv6": "2045:45::4/64",
+ "ospf": {
+ "area": "2.2.2.2",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "2.2.2.2"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "4.4.4.4",
+ "neighbors": {
+ "r2": {},
+ "r3": {},
+ "r5": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "4.4.4.4",
+ "neighbors": {
+ "r2": {},
+ "r3": {},
+ "r5": {}
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "r4": {
+ "ipv4": "10.0.45.5/24",
+ "ipv6": "2045:45::5/64",
+ "ospf": {
+ "area": "2.2.2.2",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf6": {
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point",
+ "area": "2.2.2.2"
+ }
+ }
+ },
+ "ospf": {
+ "router_id": "5.5.5.5",
+ "neighbors": {
+ "r4": {}
+ }
+ },
+ "ospf6": {
+ "router_id": "5.5.5.5",
+ "neighbors": {
+ "r4": {}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
new file mode 100644
index 0000000..07d4ca0
--- /dev/null
+++ b/tests/topotests/ospf_dual_stack/test_ospf_dual_stack.py
@@ -0,0 +1,122 @@
+#!/usr/bin/python
+
+import os
+import sys
+import time
+import pytest
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf_neighbor,
+ verify_ospf6_neighbor,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+
+def setup_module(mod):
+ """Sets up the pytest environment."""
+ 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...
+ json_file = "{}/test_ospf_dual_stack.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether OSPF converged
+ ospf_covergence_ipv4 = verify_ospf_neighbor(tgen, topo)
+ assert ospf_covergence_ipv4 is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence_ipv4
+ )
+
+ # Api call verify whether OSPF6 converged
+ ospf_covergence_ipv6 = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence_ipv6 is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence_ipv6
+ )
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+ tgen = get_topogen()
+ # Stop topology and remove tmp files
+ tgen.stop_topology()
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+#
+# ##################################
+# Test cases start here.
+# ##################################
+#
+#
+def test_ospf_dual_stack(request):
+ """OSPF test dual stack."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+
+ # Don't run this test if we have any failure.
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+
+ step("Bring up the base configuration as per the JSON topology")
+ reset_config_on_routers(tgen)
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/ospf_gr_helper.json b/tests/topotests/ospf_gr_helper/ospf_gr_helper.json
new file mode 100644
index 0000000..efd339e
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/ospf_gr_helper.json
@@ -0,0 +1,119 @@
+{
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 24,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 24
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+ "ipv4": "17.1.1.2/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 40,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv4": "17.1.1.1/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 40,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv4": "17.1.1.3/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 40,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv4": "17.1.1.4/24",
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 40,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ },
+ "opq_lsa_hex": "01005e00000570708bd051ef080045c0005cc18b0000015904f711010101e00000050204004801010101000000001e8d0000000000000000000000000001000102090300000001010101800000013bd1002c000100040000070800020001010000000003000411010101"
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
new file mode 100644
index 0000000..58d37a3
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper1.py
@@ -0,0 +1,397 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ scapy_send_raw_packet,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_gr_helper,
+ create_router_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global variables
+topo = None
+Iters = 5
+sw_name = None
+intf = None
+intf1 = None
+pkt = None
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ DUT - HR RR
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+
+TC1. Verify by default helper support is disabled for FRR ospf
+TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends
+ grace lsa, helps RR to restart gracefully.
+TC6. Verify all the show commands newly introducted as part of ospf
+ helper support - Json Key verification wrt to show commands.
+TC7. Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+TC8. Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, intf, intf1, sw_name, pkt
+ 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...
+ json_file = "{}/ospf_gr_helper.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ sw_name = "s1"
+ intf = topo["routers"]["r0"]["links"][sw_name]["interface"]
+ intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"]
+ pkt = topo["routers"]["r1"]["opq_lsa_hex"]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+def delete_ospf():
+ """delete ospf process after each test"""
+ tgen = get_topogen()
+ step("Delete ospf process")
+ for rtr in topo["routers"]:
+ ospf_del = {rtr: {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_gr_helper_tc1_p0(request):
+ """Verify by default helper support is disabled for FRR ospf"""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+
+ step("Verify that GR helper route is disabled by default to the in" "the DUT.")
+ input_dict = {
+ "helperSupport": "Disabled",
+ "strictLsaCheck": "Enabled",
+ "restartSupoort": "Planned and Unplanned Restarts",
+ "supportedGracePeriod": 1800,
+ }
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT does not enter helper mode upon receiving the " "grace lsa.")
+
+ # send grace lsa
+ scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+
+ input_dict = {"activeRestarterCnt": 1}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure graceful restart in the DUT")
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that GR helper route is enabled in the DUT.")
+ input_dict = {
+ "helperSupport": "Enabled",
+ "strictLsaCheck": "Enabled",
+ "restartSupoort": "Planned and Unplanned Restarts",
+ "supportedGracePeriod": 1800,
+ }
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Perform GR in RR.")
+ step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Unconfigure the GR helper command.")
+ ospf_gr_r0 = {
+ "r0": {
+ "ospf": {
+ "graceful-restart": {
+ "helper enable": [],
+ "opaque": True,
+ "delete": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {"helperSupport": "Disabled"}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure gr helper using the router id")
+ ospf_gr_r0 = {
+ "r0": {
+ "ospf": {"graceful-restart": {"helper enable": ["1.1.1.1"], "opaque": True}}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT does enter helper mode upon receiving" " the grace lsa.")
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Un Configure gr helper using the router id")
+ ospf_gr_r0 = {
+ "r0": {
+ "ospf": {
+ "graceful-restart": {
+ "helper enable": ["1.1.1.1"],
+ "opaque": True,
+ "delete": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that GR helper router is disabled in the DUT for" " router id x.x.x.x")
+ input_dict = {"enabledRouterIds": [{"routerId": "1.1.1.1"}]}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed, Helper role enabled for RR\n Error: {}".format(
+ tc_name, result
+ )
+ delete_ospf()
+ write_test_footer(tc_name)
+
+
+def test_ospf_gr_helper_tc2_p0(request):
+ """
+ OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
new file mode 100644
index 0000000..85646a8
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper2.py
@@ -0,0 +1,375 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ scapy_send_raw_packet,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_gr_helper,
+ create_router_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global variables
+topo = None
+Iters = 5
+sw_name = None
+intf = None
+intf1 = None
+pkt = None
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ DUT - HR RR
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+
+TC1. Verify by default helper support is disabled for FRR ospf
+TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends
+ grace lsa, helps RR to restart gracefully.
+TC6. Verify all the show commands newly introducted as part of ospf
+ helper support - Json Key verification wrt to show commands.
+TC7. Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+TC8. Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, intf, intf1, sw_name, pkt
+ 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...
+ json_file = "{}/ospf_gr_helper.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ sw_name = "s1"
+ intf = topo["routers"]["r0"]["links"][sw_name]["interface"]
+ intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"]
+ pkt = topo["routers"]["r1"]["opq_lsa_hex"]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+def delete_ospf():
+ """delete ospf process after each test"""
+ tgen = get_topogen()
+ step("Delete ospf process")
+ for rtr in topo["routers"]:
+ ospf_del = {rtr: {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_gr_helper_tc3_p1(request):
+ """
+ OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ step(
+ "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ sw_name: {
+ "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
+ "ospf": {"priority": 100},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as DR")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "Backup"},
+ "r2": {"state": "Full", "role": "DROther"},
+ "r3": {"state": "Full", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+ write_test_footer(tc_name)
+
+
+def test_ospf_gr_helper_tc4_p1(request):
+ """
+ OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ step(
+ "Configure DR pririty 100 on R0 and clear ospf neighbors " "on all the routers."
+ )
+
+ input_dict = {
+ "r0": {
+ "links": {
+ sw_name: {
+ "interface": topo["routers"]["r0"]["links"][sw_name]["interface"],
+ "ospf": {"priority": 0},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Clear ospf neighbours in all routers")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that DR election is triggered and R0 is elected as 2-Way")
+ input_dict = {
+ "r0": {
+ "ospf": {
+ "neighbors": {
+ "r1": {"state": "Full", "role": "DR"},
+ "r2": {"state": "2-Way", "role": "DROther"},
+ "r3": {"state": "2-Way", "role": "DROther"},
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_neighbor(tgen, topo, dut, input_dict, lan=True)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
new file mode 100644
index 0000000..ec97c25
--- /dev/null
+++ b/tests/topotests/ospf_gr_helper/test_ospf_gr_helper3.py
@@ -0,0 +1,327 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ scapy_send_raw_packet,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf_neighbor,
+ clear_ospf,
+ verify_ospf_gr_helper,
+ create_router_ospf,
+)
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global variables
+topo = None
+Iters = 5
+sw_name = None
+intf = None
+intf1 = None
+pkt = None
+
+"""
+Topology:
+
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ DUT - HR RR
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+Testcases:
+
+TC1. Verify by default helper support is disabled for FRR ospf
+TC2. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DR)
+TC3. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = BDR)
+TC4. OSPF GR on Broadcast : Verify DUT enters Helper mode when neighbor
+ sends grace lsa, helps RR to restart gracefully (RR = DRother)
+TC5. OSPF GR on P2P : Verify DUT enters Helper mode when neighbor sends
+ grace lsa, helps RR to restart gracefully.
+TC6. Verify all the show commands newly introducted as part of ospf
+ helper support - Json Key verification wrt to show commands.
+TC7. Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+TC8. Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, intf, intf1, sw_name, pkt
+ 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...
+ json_file = "{}/ospf_gr_helper.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ sw_name = "s1"
+ intf = topo["routers"]["r0"]["links"][sw_name]["interface"]
+ intf1 = topo["routers"]["r1"]["links"][sw_name]["interface"]
+ pkt = topo["routers"]["r1"]["opq_lsa_hex"]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module():
+ """Teardown the pytest environment"""
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+
+
+def delete_ospf():
+ """delete ospf process after each test"""
+ tgen = get_topogen()
+ step("Delete ospf process")
+ for rtr in topo["routers"]:
+ ospf_del = {rtr: {"ospf": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospf_gr_helper_tc7_p1(request):
+ """
+ Test ospf gr helper
+ Verify helper when grace lsa is received with different configured
+ value in process level (higher, lower, grace lsa timer above 1800)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step(
+ "Configure DR priority as 99 in RR , DUT dr priority = 98 "
+ "& reset ospf process in all the routers"
+ )
+ step(
+ "Enable GR on RR and DUT with grace period on RR = 333"
+ "and grace period on DUT = 300"
+ )
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {"supportedGracePeriod": 1800}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure grace period = 1801 on RR and restart ospf .")
+ grace_period_1801 = "01005e00000570708bd051ef080045c0005cbeb10000015907d111010101e00000050204004801010101000000009714000000000000000000000000000100010209030000000101010180000001c8e9002c000100040000016800020001010000000003000411010101"
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, grace_period_1801)
+
+ step("Verify R0 does not enter helper mode.")
+ input_dict = {"activeRestarterCnt": 1}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed. DUT entered helper role " " \n Error: {}".format(
+ tc_name, result
+ )
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+def test_ospf_gr_helper_tc8_p1(request):
+ """
+ Test ospf gr helper
+
+ Verify helper functionality when dut is helping RR and new grace lsa
+ is received from RR.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo, intf, intf1, pkt
+
+ step("Bring up the base config as per the topology")
+ step("Enable GR")
+ reset_config_on_routers(tgen)
+ ospf_covergence = verify_ospf_neighbor(tgen, topo, lan=True)
+ assert (
+ ospf_covergence is True
+ ), "OSPF is not after reset config \n Error:" " {}".format(ospf_covergence)
+ ospf_gr_r0 = {
+ "r0": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_gr_r1 = {
+ "r1": {"ospf": {"graceful-restart": {"helper enable": [], "opaque": True}}}
+ }
+ result = create_router_ospf(tgen, topo, ospf_gr_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {"supportedGracePeriod": 1800}
+ dut = "r0"
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that DUT enters into helper mode.")
+
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Send the Grace LSA again to DUT when RR is in GR.")
+ input_dict = {"activeRestarterCnt": 1}
+ gracelsa_sent = False
+ repeat = 0
+ dut = "r0"
+ while not gracelsa_sent and repeat < Iters:
+ gracelsa_sent = scapy_send_raw_packet(tgen, topo, "r1", intf1, pkt)
+ result = verify_ospf_gr_helper(tgen, topo, dut, input_dict)
+ if isinstance(result, str):
+ repeat += 1
+ gracelsa_sent = False
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ delete_ospf()
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_gr_topo1/__init__.py b/tests/topotests/ospf_gr_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/__init__.py
diff --git a/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..27042e1
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/ospfd.conf
@@ -0,0 +1,32 @@
+password 1
+hostname rt1
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 1
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 1.1.1.1
+ capability opaque
+ redistribute connected
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
new file mode 100644
index 0000000..d01ac74
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_database.json
@@ -0,0 +1,98 @@
+{
+ "routerId":"1.1.1.1",
+ "areas":{
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..f827581
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "converged":"Full",
+ "address":"10.0.1.2",
+ "ifaceName":"eth-rt2:10.0.1.1"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
new file mode 100644
index 0000000..548ca1e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_ospf_route.json
@@ -0,0 +1,180 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.1",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "via":"eth-rt2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
new file mode 100644
index 0000000..3dce1ee
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt1/zebra.conf b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..7b99b58
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt1/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt1
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface stub1
+ ip address 172.16.1.1/24
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..591b242
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt2
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt1
+ ip ospf network point-to-point
+ ip ospf area 1
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 2.2.2.2
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
new file mode 100644
index 0000000..40c3e82
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_database.json
@@ -0,0 +1,160 @@
+{
+ "routerId":"2.2.2.2",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.1":{
+ "routerLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"1.1.1.1",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":2
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..5a0b092
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "1.1.1.1":[
+ {
+ "converged":"Full",
+ "address":"10.0.1.1",
+ "ifaceName":"eth-rt1:10.0.1.2"
+ }
+ ],
+ "3.3.3.3":[
+ {
+ "converged":"Full",
+ "address":"10.0.2.3",
+ "ifaceName":"eth-rt3:10.0.2.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
new file mode 100644
index 0000000..4accb2b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_ospf_route.json
@@ -0,0 +1,201 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.1",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt1"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.1",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "via":"eth-rt1"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
new file mode 100644
index 0000000..8989a45
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt1"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt2/zebra.conf b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..73571aa
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt2/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt2
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt3
+ ip address 10.0.2.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..5e74771
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/ospfd.conf
@@ -0,0 +1,43 @@
+password 1
+hostname rt3
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt2
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 3.3.3.3
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
new file mode 100644
index 0000000..1fc5b54
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_database.json
@@ -0,0 +1,83 @@
+{
+ "routerId":"3.3.3.3",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..ab5e784
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_neighbor.json
@@ -0,0 +1,25 @@
+{
+ "neighbors":{
+ "2.2.2.2":[
+ {
+ "converged":"Full",
+ "address":"10.0.2.2",
+ "ifaceName":"eth-rt2:10.0.2.3"
+ }
+ ],
+ "4.4.4.4":[
+ {
+ "converged":"Full",
+ "address":"10.0.3.4",
+ "ifaceName":"eth-rt4:10.0.3.3"
+ }
+ ],
+ "6.6.6.6":[
+ {
+ "converged":"Full",
+ "address":"10.0.4.6",
+ "ifaceName":"eth-rt6:10.0.4.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
new file mode 100644
index 0000000..b2f37e2
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt2"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "via":"eth-rt2"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
new file mode 100644
index 0000000..c9a1e18
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/show_ip_route.json
@@ -0,0 +1,223 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt3/zebra.conf b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..c09bafe
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt3/zebra.conf
@@ -0,0 +1,26 @@
+password 1
+hostname rt3
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-rt2
+ ip address 10.0.2.3/24
+!
+interface eth-rt4
+ ip address 10.0.3.3/24
+!
+interface eth-rt6
+ ip address 10.0.4.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..7aa722c
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/ospfd.conf
@@ -0,0 +1,37 @@
+password 1
+hostname rt4
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 4.4.4.4
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
new file mode 100644
index 0000000..87b8041
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_database.json
@@ -0,0 +1,164 @@
+{
+ "routerId":"4.4.4.4",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..405679c
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "converged":"Full",
+ "address":"10.0.3.3",
+ "ifaceName":"eth-rt3:10.0.3.4"
+ }
+ ],
+ "5.5.5.5":[
+ {
+ "converged":"Full",
+ "address":"10.0.5.5",
+ "ifaceName":"eth-rt5:10.0.5.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
new file mode 100644
index 0000000..04e318a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_ospf_route.json
@@ -0,0 +1,202 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "via":"eth-rt5"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt5"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "via":"eth-rt3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
new file mode 100644
index 0000000..8058f8f
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.3.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt4/zebra.conf b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..e21b23e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt4/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt4
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt3
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.5.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000..0e25f1a
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/ospfd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt5
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 2
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf area 2
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 5.5.5.5
+ capability opaque
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
new file mode 100644
index 0000000..aeb8604
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_database.json
@@ -0,0 +1,102 @@
+{
+ "routerId":"5.5.5.5",
+ "areas":{
+ "0.0.0.2":{
+ "routerLinkStates":[
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"5.5.5.5",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"4.4.4.4"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"4.4.4.4"
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..893d454
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "4.4.4.4":[
+ {
+ "converged":"Full",
+ "address":"10.0.5.4",
+ "ifaceName":"eth-rt4:10.0.5.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
new file mode 100644
index 0000000..e7f712e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_ospf_route.json
@@ -0,0 +1,203 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt4"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.2",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":40,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.2",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.2",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "via":"eth-rt4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
new file mode 100644
index 0000000..9896839
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/show_ip_route.json
@@ -0,0 +1,225 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.5.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt5/zebra.conf b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..83f12c6
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt5/zebra.conf
@@ -0,0 +1,20 @@
+password 1
+hostname rt5
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt4
+ ip address 10.0.5.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
new file mode 100644
index 0000000..3960e6b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/ospfd.conf
@@ -0,0 +1,38 @@
+password 1
+hostname rt6
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 0
+!
+interface eth-rt3
+ ip ospf network point-to-point
+ ip ospf area 0
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+interface eth-rt7
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 6.6.6.6
+ capability opaque
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
new file mode 100644
index 0000000..294b2c9
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_database.json
@@ -0,0 +1,168 @@
+{
+ "routerId":"6.6.6.6",
+ "areas":{
+ "0.0.0.0":{
+ "routerLinkStates":[
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"2.2.2.2",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"3.3.3.3",
+ "numOfRouterLinks":7
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"4.4.4.4",
+ "numOfRouterLinks":3
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"7.7.7.7\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"2.2.2.2",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"4.4.4.4",
+ "summaryAddress":"10.0.5.0\/24"
+ },
+ {
+ "lsId":"10.0.6.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.6.0\/24"
+ }
+ ],
+ "asbrSummaryLinkStates":[
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"2.2.2.2"
+ }
+ ]
+ },
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"172.16.1.0",
+ "advertisedRouter":"1.1.1.1",
+ "metricType":"E2",
+ "route":"172.16.1.0\/24",
+ "tag":0
+ },
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..564a513
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_neighbor.json
@@ -0,0 +1,18 @@
+{
+ "neighbors":{
+ "3.3.3.3":[
+ {
+ "converged":"Full",
+ "address":"10.0.4.3",
+ "ifaceName":"eth-rt3:10.0.4.6"
+ }
+ ],
+ "7.7.7.7":[
+ {
+ "converged":"Full",
+ "address":"10.0.6.7",
+ "ifaceName":"eth-rt7:10.0.6.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
new file mode 100644
index 0000000..d900972
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_ospf_route.json
@@ -0,0 +1,214 @@
+{
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N",
+ "cost":20,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.0",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt7"
+ }
+ ]
+ },
+ "1.1.1.1":{
+ "routeType":"R ",
+ "cost":30,
+ "area":"0.0.0.0",
+ "IA":true,
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "2.2.2.2":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "4.4.4.4":{
+ "routeType":"R ",
+ "cost":20,
+ "area":"0.0.0.0",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "7.7.7.7":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"asbr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ },
+ "172.16.1.0\/24":{
+ "routeType":"N E2",
+ "cost":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "via":"eth-rt3"
+ }
+ ]
+ },
+ "192.168.1.0\/24":{
+ "routeType":"N E2",
+ "cost":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "via":"eth-rt7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
new file mode 100644
index 0000000..dd95f1f
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/show_ip_route.json
@@ -0,0 +1,224 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ],
+ "172.16.1.0\/24":[
+ {
+ "prefix":"172.16.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "192.168.1.0\/24":[
+ {
+ "prefix":"192.168.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.7",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt7"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt6/zebra.conf b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..67ebf14
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt6/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt6
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt3
+ ip address 10.0.4.6/24
+!
+interface eth-rt7
+ ip address 10.0.6.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
new file mode 100644
index 0000000..e52424e
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/ospfd.conf
@@ -0,0 +1,33 @@
+password 1
+hostname rt7
+log file ospfd.log
+log commands
+!
+! debug ospf zebra
+! debug ospf event
+! debug ospf lsa
+! debug ospf te
+! debug ospf packet all
+! debug ospf packet ls-update detail
+! debug ospf ism
+! debug ospf nsm
+! debug ospf nssa
+! debug ospf graceful-restart
+!
+interface lo
+ ip ospf area 3
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf area 3
+ ip ospf hello-interval 3
+ ip ospf dead-interval 9
+!
+router ospf
+ router-id 7.7.7.7
+ capability opaque
+ redistribute connected
+ area 3 nssa
+ graceful-restart grace-period 120
+ graceful-restart helper enable
+!
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
new file mode 100644
index 0000000..4916fba
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_database.json
@@ -0,0 +1,99 @@
+{
+ "routerId":"7.7.7.7",
+ "areas":{
+ "0.0.0.3":{
+ "routerLinkStates":[
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "numOfRouterLinks":2
+ },
+ {
+ "lsId":"7.7.7.7",
+ "advertisedRouter":"7.7.7.7",
+ "numOfRouterLinks":3
+ }
+ ],
+ "summaryLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"0.0.0.0\/0"
+ },
+ {
+ "lsId":"1.1.1.1",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"1.1.1.1\/32"
+ },
+ {
+ "lsId":"2.2.2.2",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"2.2.2.2\/32"
+ },
+ {
+ "lsId":"3.3.3.3",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"3.3.3.3\/32"
+ },
+ {
+ "lsId":"4.4.4.4",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"4.4.4.4\/32"
+ },
+ {
+ "lsId":"5.5.5.5",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"5.5.5.5\/32"
+ },
+ {
+ "lsId":"6.6.6.6",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"6.6.6.6\/32"
+ },
+ {
+ "lsId":"10.0.1.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.1.0\/24"
+ },
+ {
+ "lsId":"10.0.2.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.2.0\/24"
+ },
+ {
+ "lsId":"10.0.3.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.3.0\/24"
+ },
+ {
+ "lsId":"10.0.4.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.4.0\/24"
+ },
+ {
+ "lsId":"10.0.5.0",
+ "advertisedRouter":"6.6.6.6",
+ "summaryAddress":"10.0.5.0\/24"
+ }
+ ],
+ "nssaExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+ }
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"192.168.1.0",
+ "advertisedRouter":"7.7.7.7",
+ "metricType":"E2",
+ "route":"192.168.1.0\/24",
+ "tag":0
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
new file mode 100644
index 0000000..bc6b606
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_neighbor.json
@@ -0,0 +1,11 @@
+{
+ "neighbors":{
+ "6.6.6.6":[
+ {
+ "converged":"Full",
+ "address":"10.0.6.6",
+ "ifaceName":"eth-rt6:10.0.6.7"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
new file mode 100644
index 0000000..89bad32
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_ospf_route.json
@@ -0,0 +1,168 @@
+{
+ "0.0.0.0\/0":{
+ "routeType":"N IA",
+ "cost":11,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "1.1.1.1\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "2.2.2.2\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "3.3.3.3\/32":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "4.4.4.4\/32":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "5.5.5.5\/32":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6\/32":{
+ "routeType":"N IA",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "7.7.7.7\/32":{
+ "routeType":"N",
+ "cost":0,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"lo"
+ }
+ ]
+ },
+ "10.0.1.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.2.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.3.0\/24":{
+ "routeType":"N IA",
+ "cost":30,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.4.0\/24":{
+ "routeType":"N IA",
+ "cost":20,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.5.0\/24":{
+ "routeType":"N IA",
+ "cost":40,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ },
+ "10.0.6.0\/24":{
+ "routeType":"N",
+ "cost":10,
+ "area":"0.0.0.3",
+ "nexthops":[
+ {
+ "ip":" ",
+ "directly attached to":"eth-rt6"
+ }
+ ]
+ },
+ "6.6.6.6":{
+ "routeType":"R ",
+ "cost":10,
+ "area":"0.0.0.3",
+ "routerType":"abr",
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "via":"eth-rt6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
new file mode 100644
index 0000000..0fb906b
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/show_ip_route.json
@@ -0,0 +1,210 @@
+{
+ "0.0.0.0\/0":[
+ {
+ "prefix":"0.0.0.0\/0",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":11,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "7.7.7.7\/32":[
+ {
+ "prefix":"7.7.7.7\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":0,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":20,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "ip":"10.0.6.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_gr_topo1/rt7/zebra.conf b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
new file mode 100644
index 0000000..7037e6f
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/rt7/zebra.conf
@@ -0,0 +1,23 @@
+password 1
+hostname rt7
+log file zebra.log
+log commands
+!
+! debug zebra event
+! debug zebra packet
+! debug zebra rib
+! debug zebra kernel
+!
+interface lo
+ ip address 7.7.7.7/32
+!
+interface stub1
+ ip address 192.168.1.1/24
+!
+interface eth-rt6
+ ip address 10.0.6.7/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
new file mode 100755
index 0000000..debf7ad
--- /dev/null
+++ b/tests/topotests/ospf_gr_topo1/test_ospf_gr_topo1.py
@@ -0,0 +1,462 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_gr_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 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_ospf_gr_topo1.py:
+
+ +---------+
+ | RT1 |
+ | 1.1.1.1 |
+ +---------+
+ |eth-rt2
+ |
+ |10.0.1.0/24
+ |
+ |eth-rt1
+ +---------+
+ | RT2 |
+ | 2.2.2.2 |
+ +---------+
+ |eth-rt3
+ |
+ |10.0.2.0/24
+ |
+ |eth-rt2
+ +---------+
+ | RT3 |
+ | 3.3.3.3 |
+ +---------+
+ eth-rt4| |eth-rt6
+ | |
+ 10.0.3.0/24 | | 10.0.4.0/24
+ +---------+ +--------+
+ | |
+ |eth-rt3 |eth-rt3
+ +---------+ +---------+
+ | RT4 | | RT6 |
+ | 4.4.4.4 | | 6.6.6.6 |
+ +---------+ +---------+
+ |eth-rt5 |eth-rt7
+ | |
+ |10.0.5.0/24 |10.0.6.0/24
+ | |
+ |eth-rt4 |eth-rt6
+ +---------+ +---------+
+ | RT5 | | RT7 |
+ | 5.5.5.5 | | 7.7.7.7 |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+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 (
+ kill_router_daemons,
+ start_router_daemons,
+)
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd]
+
+# Global multi-dimensional dictionary containing all expected outputs
+outputs = {}
+
+
+def build_topo(tgen):
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="stub1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt7")
+ switch.add_link(tgen.gears["rt7"], nodeif="eth-rt6")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt7"], nodeif="stub1")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference, tries):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=tries, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def expect_grace_lsa(restarting, area, helper):
+ """
+ Check if the given helper neighbor has already received a Grace-LSA from
+ the router performing a graceful restart.
+ """
+ tgen = get_topogen()
+
+ logger.info(
+ "'{}': checking if a Grace-LSA was received from '{}'".format(
+ helper, restarting
+ )
+ )
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[helper],
+ "show ip ospf database opaque-link json",
+ {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ area: [
+ {
+ "advertisingRouter": restarting,
+ "opaqueType": "Grace-LSA",
+ }
+ ]
+ }
+ }
+ },
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=60, wait=1)
+ assertmsg = '"{}" didn\'t receive a Grace-LSA from "{}"'.format(helper, restarting)
+
+ assert result is None, assertmsg
+
+
+def check_routers(initial_convergence=False, exiting=None, restarting=None):
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "rt7"]:
+ # Check the RIB first, which should be preserved across restarts in
+ # all routers of the routing domain.
+ # If we are not on initial convergence *but* we are checking
+ # after a restart. Looking in the zebra rib for installed
+ # is a recipe for test failure. Why? because if we are restarting
+ # then ospf is in the process of establishing neighbors and passing
+ # new routes to zebra. Zebra will not mark the route as installed
+ # when it receives a replacement from ospf until it has finished
+ # processing it. Let's give it a few seconds to allow this to happen
+ # under load.
+ if initial_convergence == True:
+ tries = 240
+ else:
+ if restarting != None:
+ tries = 60
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip route ospf json", "show_ip_route.json", tries
+ )
+
+ # Check that all adjacencies are up and running (except when there's
+ # an OSPF instance that is shutting down).
+ if exiting == None:
+ tries = 240
+ router_compare_json_output(
+ rname, "show ip ospf neighbor json", "show_ip_ospf_neighbor.json", tries
+ )
+
+ # Check the OSPF RIB and LSDB.
+ # In the restarting router, wait up to one minute for the LSDB to converge.
+ if exiting != rname:
+ if initial_convergence == True or restarting == rname:
+ tries = 240
+ else:
+ tries = 1
+ router_compare_json_output(
+ rname, "show ip ospf database json", "show_ip_ospf_database.json", tries
+ )
+ router_compare_json_output(
+ rname, "show ip ospf route json", "show_ip_ospf_route.json", tries
+ )
+
+
+def ensure_gr_is_in_zebra(rname):
+ retry = True
+ retry_times = 10
+ tgen = get_topogen()
+
+ while retry and retry_times > 0:
+ out = tgen.net[rname].cmd(
+ 'vtysh -c "show zebra client" | grep "Client: ospf$" -A 40 | grep "Capabilities "'
+ )
+
+ if "Graceful Restart" not in out:
+ sleep(2)
+ retry_times -= 1
+ else:
+ retry = False
+
+ assertmsg = "%s does not appear to have Graceful Restart setup" % rname
+ assert not retry and retry_times > 0, assertmsg
+
+
+#
+# Test initial network convergence
+#
+def test_initial_convergence():
+ logger.info("Test: verify initial network convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_routers(initial_convergence=True)
+
+
+#
+# Test rt1 performing a graceful restart
+#
+def test_gr_rt1():
+ logger.info("Test: verify rt1 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt1"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="1.1.1.1", area="0.0.0.1", helper="rt2")
+ ensure_gr_is_in_zebra("rt1")
+ kill_router_daemons(tgen, "rt1", ["ospfd"], save_config=False)
+ check_routers(exiting="rt1")
+
+ start_router_daemons(tgen, "rt1", ["ospfd"])
+ check_routers(restarting="rt1")
+
+
+#
+# Test rt2 performing a graceful restart
+#
+def test_gr_rt2():
+ logger.info("Test: verify rt2 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt2"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.1", helper="rt1")
+ expect_grace_lsa(restarting="2.2.2.2", area="0.0.0.0", helper="rt3")
+ ensure_gr_is_in_zebra("rt2")
+ kill_router_daemons(tgen, "rt2", ["ospfd"], save_config=False)
+ check_routers(exiting="rt2")
+
+ start_router_daemons(tgen, "rt2", ["ospfd"])
+ check_routers(restarting="rt2")
+
+
+#
+# Test rt3 performing a graceful restart
+#
+def test_gr_rt3():
+ logger.info("Test: verify rt3 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt3"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt2")
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt4")
+ expect_grace_lsa(restarting="3.3.3.3", area="0.0.0.0", helper="rt6")
+ ensure_gr_is_in_zebra("rt3")
+ kill_router_daemons(tgen, "rt3", ["ospfd"], save_config=False)
+ check_routers(exiting="rt3")
+
+ start_router_daemons(tgen, "rt3", ["ospfd"])
+ check_routers(restarting="rt3")
+
+
+#
+# Test rt4 performing a graceful restart
+#
+def test_gr_rt4():
+ logger.info("Test: verify rt4 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt4"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.0", helper="rt3")
+ expect_grace_lsa(restarting="4.4.4.4", area="0.0.0.2", helper="rt5")
+ ensure_gr_is_in_zebra("rt4")
+ kill_router_daemons(tgen, "rt4", ["ospfd"], save_config=False)
+ check_routers(exiting="rt4")
+
+ start_router_daemons(tgen, "rt4", ["ospfd"])
+ check_routers(restarting="rt4")
+
+
+#
+# Test rt5 performing a graceful restart
+#
+def test_gr_rt5():
+ logger.info("Test: verify rt5 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt5"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="5.5.5.5", area="0.0.0.2", helper="rt4")
+ ensure_gr_is_in_zebra("rt5")
+ kill_router_daemons(tgen, "rt5", ["ospfd"], save_config=False)
+ check_routers(exiting="rt5")
+
+ start_router_daemons(tgen, "rt5", ["ospfd"])
+ check_routers(restarting="rt5")
+
+
+#
+# Test rt6 performing a graceful restart
+#
+def test_gr_rt6():
+ logger.info("Test: verify rt6 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt6"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.0", helper="rt3")
+ expect_grace_lsa(restarting="6.6.6.6", area="0.0.0.3", helper="rt7")
+ ensure_gr_is_in_zebra("rt6")
+ kill_router_daemons(tgen, "rt6", ["ospfd"], save_config=False)
+ check_routers(exiting="rt6")
+
+ start_router_daemons(tgen, "rt6", ["ospfd"])
+ check_routers(restarting="rt6")
+
+
+#
+# Test rt7 performing a graceful restart
+#
+def test_gr_rt7():
+ logger.info("Test: verify rt7 performing a graceful restart")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ tgen.net["rt7"].cmd('vtysh -c "graceful-restart prepare ip ospf"')
+ expect_grace_lsa(restarting="7.7.7.7", area="0.0.0.3", helper="rt6")
+ ensure_gr_is_in_zebra("rt7")
+ kill_router_daemons(tgen, "rt7", ["ospfd"], save_config=False)
+ check_routers(exiting="rt7")
+
+ start_router_daemons(tgen, "rt7", ["ospfd"])
+ check_routers(restarting="rt7")
+
+
+# Memory leak test template
+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/ospf_instance_redistribute/r1/ospf_default_information.json b/tests/topotests/ospf_instance_redistribute/r1/ospf_default_information.json
new file mode 100644
index 0000000..c0f71bb
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/ospf_default_information.json
@@ -0,0 +1,33 @@
+{
+ "ospfInstance":3,
+ "routerId":"192.168.100.1",
+ "areas":{
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"0.0.0.0",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"0.0.0.0/0",
+ "tag":0
+ },
+ {
+ "lsId":"4.5.6.7",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"4.5.6.7/32",
+ "tag":0
+ },
+ {
+ "lsId":"4.5.6.10",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"4.5.6.10/32",
+ "tag":0
+ }
+ ],
+ "asExternalLinkStatesCount":3
+}
diff --git a/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa.json b/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa.json
new file mode 100644
index 0000000..fc8e349
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa.json
@@ -0,0 +1,17 @@
+{
+ "ospfInstance":3,
+ "routerId":"192.168.100.1",
+ "areas":{
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"4.5.6.7",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"4.5.6.7/32",
+ "tag":0
+ }
+ ],
+ "asExternalLinkStatesCount":1
+}
diff --git a/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa2.json b/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa2.json
new file mode 100644
index 0000000..b7d6f7c
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/ospf_instance_lsa2.json
@@ -0,0 +1,25 @@
+{
+ "ospfInstance":3,
+ "routerId":"192.168.100.1",
+ "areas":{
+ },
+ "asExternalLinkStates":[
+ {
+ "lsId":"4.5.6.7",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"4.5.6.7/32",
+ "tag":0
+ },
+ {
+ "lsId":"4.5.6.10",
+ "advertisedRouter":"192.168.100.1",
+ "sequenceNumber":"80000001",
+ "metricType":"E2",
+ "route":"4.5.6.10/32",
+ "tag":0
+ }
+ ],
+ "asExternalLinkStatesCount":2
+}
diff --git a/tests/topotests/ospf_instance_redistribute/r1/ospfd-3.conf b/tests/topotests/ospf_instance_redistribute/r1/ospfd-3.conf
new file mode 100644
index 0000000..88432fe
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/ospfd-3.conf
@@ -0,0 +1,2 @@
+router ospf 3
+ redistribute sharp
diff --git a/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json
new file mode 100644
index 0000000..c0a1f6a
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/sharp_installed.json
@@ -0,0 +1,13 @@
+{
+ "routes":[
+ {
+ "fib":3,
+ "rib":3,
+ "fibOffLoaded":0,
+ "fibTrapped":0,
+ "type":"sharp"
+ }
+ ],
+ "routesTotal":4,
+ "routesTotalFib":4
+}
diff --git a/tests/topotests/ospf_instance_redistribute/r1/zebra.conf b/tests/topotests/ospf_instance_redistribute/r1/zebra.conf
new file mode 100644
index 0000000..6bb2a65
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/r1/zebra.conf
@@ -0,0 +1,2 @@
+interface lo
+ ip address 192.168.100.1/24
diff --git a/tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py b/tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py
new file mode 100644
index 0000000..88c236d
--- /dev/null
+++ b/tests/topotests/ospf_instance_redistribute/test_ospf_instance_redistribute.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_instance_redistribute.py
+#
+# Copyright (c) 2022 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+# 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_ospf_instance_redistribute
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.sharpd]
+
+# 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 functools import partial
+
+# Required to instantiate the topology builder class.
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+ # Connect r1 and r2 through the eth0 interface
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ r1 = tgen.gears["r1"]
+ r1.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "r1/zebra.conf")
+ )
+ r1.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "r1/ospfd-3.conf"),
+ "-n 3"
+ )
+ r1.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "r1/sharpd.conf")
+ )
+
+ 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_install_sharp_instance_routes():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Installing sharp routes")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("sharp install route 4.5.6.7 nexthop 192.168.100.2 1")
+ r1.vtysh_cmd("sharp install route 4.5.6.8 nexthop 192.168.100.2 1 instance 3")
+ r1.vtysh_cmd("sharp install route 4.5.6.9 nexthop 192.168.100.3 1 instance 4")
+ r1.vtysh_cmd("conf\nrouter ospf 3\nredistribute sharp")
+
+ json_file = "{}/r1/sharp_installed.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route summ json", expected)
+
+ logger.info("Ensuring that they exist in the rib/fib")
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"r1" sharp routes are not installed'
+ assert result is None, assertmsg
+
+def test_ospf_instance_redistribute():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing that ospf instance 3 has the redistributed sharp route")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\nrouter ospf 3\nredistribute sharp")
+
+ json_file = "{}/r1/ospf_instance_lsa.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf 3 data json", expected)
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"r1" ospf instance 3 does not have the proper redistributed routes'
+ assert result is None, assertmsg
+
+ r1.vtysh_cmd("sharp install route 4.5.6.10 nexthop 192.168.100.2 1")
+ r1.vtysh_cmd("sharp install route 4.5.6.11 nexthop 192.168.100.2 1 instance 3")
+ r1.vtysh_cmd("sharp install route 4.5.6.12 nexthop 192.168.100.2 1 instance 4")
+
+ logger.info("Added new sharp routes let's see if we pick up only the .10")
+ json_file = "{}/r1/ospf_instance_lsa2.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf 3 data json", expected)
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"r1" ospf instance 3 does not have the proper redistributed routes'
+ assert result is None, assertmsg
+
+
+def test_ospf_instance_default_information():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Testing the using default information originate")
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\nrouter ospf 3\ndefault-information originate")
+
+ r1.vtysh_cmd("conf\nip route 0.0.0.0/0 192.168.100.2")
+ json_file = "{}/r1/ospf_default_information.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip ospf 3 data json", expected)
+
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"r1" ospf instance 3 does not properly redistribute the default route'
+ assert result is None, assertmsg
+
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py b/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/__init__.py
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf
new file mode 100644
index 0000000..e365e25
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/frr.conf
@@ -0,0 +1,57 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-frr.log
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.20.1/24
+!
+interface r1-eth2 vrf neno
+ ip address 10.0.30.1/24
+!
+ip forwarding
+!
+router ospf
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.1.0/24 area 0
+ network 10.0.20.0/24 area 0
+!
+router ospf vrf neno
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.30.0/24 area 0
+!
+!
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf neno
+ !
+!
+router bgp 99 vrf neno
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf route-map rmap
+ import vrf default
+ !
+!
+!!!!!!!!!!!!!!!!!!!!!
+! VRFs neno and ray subnets
+ip prefix-list nets seq 5 permit 10.0.3.0/24
+ip prefix-list nets seq 10 permit 10.0.30.0/24
+ip prefix-list nets seq 15 permit 10.0.4.0/24
+ip prefix-list nets seq 20 permit 10.0.40.0/24
+ip prefix-list nets seq 25 deny any
+!
+route-map rmap permit 10
+ match ip address prefix-list nets
+ exit
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt
new file mode 100644
index 0000000..45b7fad
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-default.txt
@@ -0,0 +1,19 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.20.2, r1-eth1
+N 10.0.20.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.20.2, r1-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.4.0/24 [10/20] tag: 0
+ via 10.0.20.2, r1-eth1
+N E2 10.0.40.0/24 [10/20] tag: 0
+ via 10.0.20.2, r1-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt
new file mode 100644
index 0000000..cc2c1ba
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/ospf-vrf-neno.txt
@@ -0,0 +1,12 @@
+VRF Name: neno
+============ OSPF network routing table ============
+N 10.0.3.0/24 [20] area: 0.0.0.0
+ via 10.0.30.3, r1-eth2
+N 10.0.30.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.3 [10] area: 0.0.0.0, ASBR
+ via 10.0.30.3, r1-eth2
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt
new file mode 100644
index 0000000..86c089a
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-default.txt
@@ -0,0 +1,9 @@
+O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX
+C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX
+O>* 10.0.2.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
+B>* 10.0.3.0/24 [20/20] via 10.0.30.3, r1-eth2 (vrf neno), weight 1, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
+O 10.0.20.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX
+C>* 10.0.20.0/24 is directly connected, r1-eth1, XX:XX:XX
+B>* 10.0.30.0/24 [20/0] is directly connected, r1-eth2 (vrf neno), weight 1, XX:XX:XX
+O>* 10.0.40.0/24 [110/20] via 10.0.20.2, r1-eth1, weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt
new file mode 100644
index 0000000..4e818eb
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r1/zebra-vrf-neno.txt
@@ -0,0 +1,6 @@
+VRF neno:
+O>* 10.0.3.0/24 [110/20] via 10.0.30.3, r1-eth2, weight 1, XX:XX:XX
+B>* 10.0.4.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX
+O 10.0.30.0/24 [110/10] is directly connected, r1-eth2, weight 1, XX:XX:XX
+C>* 10.0.30.0/24 is directly connected, r1-eth2, XX:XX:XX
+B>* 10.0.40.0/24 [20/20] via 10.0.20.2, r1-eth1 (vrf default), weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf
new file mode 100644
index 0000000..e87899c
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/frr.conf
@@ -0,0 +1,62 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-frr.log
+!
+interface r2-eth0
+ ip address 10.0.2.2/24
+!
+interface r2-eth1
+ ip address 10.0.20.2/24
+!
+ip route 0.0.0.0/0 10.0.20.1
+!
+interface r2-eth2 vrf ray
+ ip address 10.0.40.2/24
+!
+ip forwarding
+!
+vrf ray
+ ip protocol bgp route-map rmap
+ exit-vrf
+!
+router ospf
+ ospf router-id 10.0.255.2
+ redistribute bgp
+ network 10.0.2.0/24 area 0
+ network 10.0.20.0/24 area 0
+!
+router ospf vrf ray
+ ospf router-id 10.0.255.1
+ redistribute bgp
+ network 10.0.40.0/24 area 0
+!
+
+router bgp 99
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf ray
+ !
+!
+router bgp 99 vrf ray
+ no bgp ebgp-requires-policy
+ address-family ipv4 unicast
+ redistribute connected
+ redistribute ospf
+ import vrf default
+ !
+!
+!!!!!!!!!!!!!!!!!!!!!
+! VRFs neno and ray subnets
+ip prefix-list nets seq 5 permit 10.0.3.0/24
+ip prefix-list nets seq 10 permit 10.0.30.0/24
+ip prefix-list nets seq 15 permit 10.0.4.0/24
+ip prefix-list nets seq 20 permit 10.0.40.0/24
+ip prefix-list nets seq 25 deny any
+!
+route-map rmap permit 10
+ match ip address prefix-list nets
+ exit
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt
new file mode 100644
index 0000000..77b8038
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-default.txt
@@ -0,0 +1,20 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.20.1, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.20.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.20.1, r2-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.3.0/24 [10/20] tag: 0
+ via 10.0.20.1, r2-eth1
+N E2 10.0.30.0/24 [10/20] tag: 0
+ via 10.0.20.1, r2-eth1
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt
new file mode 100644
index 0000000..b70ee9d
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/ospf-vrf-ray.txt
@@ -0,0 +1,15 @@
+VRF Name: ray
+============ OSPF network routing table ============
+N 10.0.4.0/24 [20] area: 0.0.0.0
+ via 10.0.40.4, r2-eth2
+N 10.0.40.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.4 [10] area: 0.0.0.0, ASBR
+ via 10.0.40.4, r2-eth2
+
+============ OSPF external routing table ===========
+
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt
new file mode 100644
index 0000000..9681d8a
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-default.txt
@@ -0,0 +1,10 @@
+S>* 0.0.0.0/0 [1/0] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+O>* 10.0.1.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX
+C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX
+O>* 10.0.3.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+B>* 10.0.4.0/24 [20/20] via 10.0.40.4, r2-eth2 (vrf ray), weight 1, XX:XX:XX
+O 10.0.20.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX
+C>* 10.0.20.0/24 is directly connected, r2-eth1, XX:XX:XX
+O>* 10.0.30.0/24 [110/20] via 10.0.20.1, r2-eth1, weight 1, XX:XX:XX
+B>* 10.0.40.0/24 [20/0] is directly connected, r2-eth2 (vrf ray), weight 1, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt
new file mode 100644
index 0000000..ce9903a
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r2/zebra-vrf-ray.txt
@@ -0,0 +1,9 @@
+VRF ray:
+B 10.0.1.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX
+B 10.0.2.0/24 [20/0] is directly connected, r2-eth0 (vrf default) inactive, weight 1, XX:XX:XX
+B>* 10.0.3.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.40.4, r2-eth2, weight 1, XX:XX:XX
+B 10.0.20.0/24 [20/0] is directly connected, r2-eth1 (vrf default) inactive, weight 1, XX:XX:XX
+B>* 10.0.30.0/24 [20/20] via 10.0.20.1, r2-eth1 (vrf default), weight 1, XX:XX:XX
+O 10.0.40.0/24 [110/10] is directly connected, r2-eth2, weight 1, XX:XX:XX
+C>* 10.0.40.0/24 is directly connected, r2-eth2, XX:XX:XX
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf
new file mode 100644
index 0000000..2657f58
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/frr.conf
@@ -0,0 +1,22 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-frr.log
+!
+interface r3-eth0
+ ip address 10.0.3.3/24
+!
+interface r3-eth1
+ ip address 10.0.30.3/24
+!
+ip forwarding
+!
+!
+router ospf
+ ospf router-id 10.0.255.3
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.3.0/24 area 0
+ network 10.0.30.0/24 area 0
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt
new file mode 100644
index 0000000..3eb5690
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/ospf-vrf-default.txt
@@ -0,0 +1,17 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth0
+N 10.0.30.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.30.1, r3-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.4.0/24 [10/20] tag: 0
+ via 10.0.30.1, r3-eth1
+N E2 10.0.40.0/24 [10/20] tag: 0
+ via 10.0.30.1, r3-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt
new file mode 100644
index 0000000..f6f861b
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r3/zebra-vrf-default.txt
@@ -0,0 +1,8 @@
+O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX
+O>* 10.0.4.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX
+O 10.0.30.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX
+C>* 10.0.30.0/24 is directly connected, r3-eth1, XX:XX:XX
+O>* 10.0.40.0/24 [110/20] via 10.0.30.1, r3-eth1, weight 1, XX:XX:XX
+
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf
new file mode 100644
index 0000000..79d8077
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/frr.conf
@@ -0,0 +1,22 @@
+!
+hostname r4
+password zebra
+log file /tmp/r4-frr.log
+!
+interface r4-eth0
+ ip address 10.0.4.4/24
+!
+interface r4-eth1
+ ip address 10.0.40.4/24
+!
+ip forwarding
+!
+!
+router ospf
+ ospf router-id 10.0.255.4
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.4.0/24 area 0
+ network 10.0.40.0/24 area 0
+!
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt
new file mode 100644
index 0000000..ad799af
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/ospf-vrf-default.txt
@@ -0,0 +1,17 @@
+VRF Name: default
+============ OSPF network routing table ============
+N 10.0.4.0/24 [10] area: 0.0.0.0
+ directly attached to r4-eth0
+N 10.0.40.0/24 [10] area: 0.0.0.0
+ directly attached to r4-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.40.2, r4-eth1
+
+============ OSPF external routing table ===========
+N E2 10.0.3.0/24 [10/20] tag: 0
+ via 10.0.40.2, r4-eth1
+N E2 10.0.30.0/24 [10/20] tag: 0
+ via 10.0.40.2, r4-eth1
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt
new file mode 100644
index 0000000..b6be5e7
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/r4/zebra-vrf-default.txt
@@ -0,0 +1,7 @@
+O>* 10.0.3.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX
+O 10.0.4.0/24 [110/10] is directly connected, r4-eth0, weight 1, XX:XX:XX
+C>* 10.0.4.0/24 is directly connected, r4-eth0, XX:XX:XX
+O>* 10.0.30.0/24 [110/20] via 10.0.40.2, r4-eth1, weight 1, XX:XX:XX
+O 10.0.40.0/24 [110/10] is directly connected, r4-eth1, weight 1, XX:XX:XX
+C>* 10.0.40.0/24 is directly connected, r4-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py b/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py
new file mode 100644
index 0000000..0b6b50e
--- /dev/null
+++ b/tests/topotests/ospf_multi_vrf_bgp_route_leak/test_ospf_multi_vrf_bgp_route_leak.py
@@ -0,0 +1,243 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_multi_vrf_bgp_route_leak.py
+#
+# Copyright (c) 2022 ATCorp
+# Jafar Al-Gharaibeh
+#
+# 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.
+#
+
+import os
+import sys
+from functools import partial
+import pytest
+
+# 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
+
+
+"""
+test_ospf_multi_vrf_bgp_route_leak.py: Test OSPF with multi vrf setup and route leaking.
+"""
+
+TOPOLOGY = """
+ bgp route leaking (connected/ospf), vrfs: neno <==> default <==> ray
+ routes leaking to vrfs are limited to neno and ray routes.
+
+ 10.0.1.1/24
+ ^
+ |vrf:default
+ +---+---+
+ 10.0.30.0/24 .1| |.1
+ +-----------------+ R1 +
+ | vrf:neno | |
+ | +---+---+ ^
+ |.3 .1|vrf:default | 10.0.4.4/24
+ +---+---+ | +---+---+
+ | | 10.0.20.0/24 | |
+ | R3 | | | R4 |
+ | | |.2 | |
+ +---+---+ +---+---+ +---+---+
+ | | | vrf:ray |.4
+ v | R2 +----------------+
+10.0.3.3/24 | |.2 10.0.40.0/24
+ +---+---+
+ |
+ v
+ 10.0.2.2/24
+
+"""
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.bgpd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+
+ # Create a empty network for router 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+
+ # Create a empty network for router 4
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+
+ # Interconect router 1, 2
+ switch = tgen.add_switch("s1-2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 3
+ switch = tgen.add_switch("s1-3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Interconect router 2, 4
+ switch = tgen.add_switch("s2-4")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ logger.info("OSPF Multi VRF Topology with BGP route leaking:\n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ r1_vrf_setup_cmds = [
+ "ip link add name neno type vrf table 11",
+ "ip link set dev neno up",
+ "ip link set dev r1-eth2 vrf neno up",
+ ]
+ r2_vrf_setup_cmds = [
+ "ip link add name ray type vrf table 11",
+ "ip link set dev ray up",
+ "ip link set dev r2-eth2 vrf ray up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRFs on r1/r2 and bind to interfaces
+ for cmd in r1_vrf_setup_cmds:
+ tgen.net["r1"].cmd(cmd)
+ for cmd in r2_vrf_setup_cmds:
+ tgen.net["r2"].cmd(cmd)
+
+ logger.info("Testing OSPF VRF support")
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_frr_config(os.path.join(CWD, "{}/frr.conf".format(rname)))
+
+ # Initialize all routers.
+ tgen.start_router()
+ for router in router_list.values():
+ if router.has_version("<", "4.0"):
+ tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+# Shared test function to validate expected output.
+def compare_show_ip_route_vrf(rname, expected, vrf_name):
+ """
+ Calls 'show ip route vrf [vrf_name] route' and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+ current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name)
+ ret = topotest.difflines(
+ current, expected, title1="Current output", title2="Expected output"
+ )
+ return ret
+
+
+def test_ospf_convergence():
+ "Test OSPF daemon convergence"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rname, router in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', rname)
+
+ for vrf in ["default", "neno", "ray"]:
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospf-vrf-{}.txt".format(rname, vrf))
+ if vrf == "default" or os.path.exists(reffile):
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ topotest.router_output_cmp,
+ router,
+ "show ip ospf vrf {} route".format(vrf),
+ expected,
+ )
+ result, diff = topotest.run_and_expect(
+ test_func, "", count=80, wait=1
+ )
+ assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_ospf_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name)
+ for vrf in ["default", "neno", "ray"]:
+ reffile = os.path.join(CWD, "{}/zebra-vrf-{}.txt".format(router.name, vrf))
+ if vrf == "default" or os.path.exists(reffile):
+ expected = open(reffile).read()
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ compare_show_ip_route_vrf, router.name, expected, vrf
+ )
+ result, diff = topotest.run_and_expect(
+ test_func, "", count=80, wait=1
+ )
+ assertmsg = 'OSPF IPv4 route mismatch in router "{}": {}'.format(
+ router.name, diff
+ )
+ assert result, assertmsg
+
+
+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/ospf_netns_vrf/__init__.py b/tests/topotests/ospf_netns_vrf/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/__init__.py
diff --git a/tests/topotests/ospf_netns_vrf/r1/ospfd.conf b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf
new file mode 100644
index 0000000..e1e2bfb
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/ospfd.conf
@@ -0,0 +1,13 @@
+!
+hostname r1
+password zebra
+log file /tmp/r1-ospfd.log
+!
+router ospf vrf r1-ospf-cust1
+ ospf router-id 10.0.255.1
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.1.0/24 area 0
+ network 10.0.3.0/24 area 0
+!
diff --git a/tests/topotests/ospf_netns_vrf/r1/ospfroute.txt b/tests/topotests/ospf_netns_vrf/r1/ospfroute.txt
new file mode 100644
index 0000000..d617ab3
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/ospfroute.txt
@@ -0,0 +1,18 @@
+VRF Name: r1-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r1-eth1
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+N 10.0.10.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r1-eth1
+R 10.0.255.3 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.1, r1-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r1/ospfroute_down.txt b/tests/topotests/ospf_netns_vrf/r1/ospfroute_down.txt
new file mode 100644
index 0000000..4f7fd69
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/ospfroute_down.txt
@@ -0,0 +1,14 @@
+VRF Name: r1-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r1-eth1
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r1-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r1/zebra.conf b/tests/topotests/ospf_netns_vrf/r1/zebra.conf
new file mode 100644
index 0000000..56d7a97
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/zebra.conf
@@ -0,0 +1,17 @@
+! debug zebra kernel
+! debug zebra dplane detail
+! debug zebra rib
+! debug zebra event
+!
+hostname r1
+password zebra
+log file /tmp/r1-zebra.log
+!
+interface r1-eth0 vrf r1-ospf-cust1
+ ip address 10.0.1.1/24
+!
+interface r1-eth1 vrf r1-ospf-cust1
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt
new file mode 100644
index 0000000..979af20
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/zebraroute.txt
@@ -0,0 +1,8 @@
+VRF r1-ospf-cust1:
+O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX
+C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX
+O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX
+O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX
+O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r1-eth1, weight 1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt
new file mode 100644
index 0000000..ec99fad
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r1/zebraroutedown.txt
@@ -0,0 +1,7 @@
+VRF r1-ospf-cust1:
+O 10.0.1.0/24 [110/10] is directly connected, r1-eth0, weight 1, XX:XX:XX
+C>* 10.0.1.0/24 is directly connected, r1-eth0, XX:XX:XX
+O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r1-eth1, weight 1, XX:XX:XX
+O 10.0.3.0/24 [110/10] is directly connected, r1-eth1, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r1-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/r2/ospfd.conf b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf
new file mode 100644
index 0000000..c198427
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/ospfd.conf
@@ -0,0 +1,14 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-ospfd.log
+!
+!
+router ospf vrf r2-ospf-cust1
+ ospf router-id 10.0.255.2
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.2.0/24 area 0
+ network 10.0.3.0/24 area 0
+!
diff --git a/tests/topotests/ospf_netns_vrf/r2/ospfroute.txt b/tests/topotests/ospf_netns_vrf/r2/ospfroute.txt
new file mode 100644
index 0000000..89763ff
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/ospfroute.txt
@@ -0,0 +1,18 @@
+VRF Name: r2-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+N 10.0.10.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r2-eth1
+R 10.0.255.3 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.1, r2-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r2/ospfroute_down.txt b/tests/topotests/ospf_netns_vrf/r2/ospfroute_down.txt
new file mode 100644
index 0000000..d946f02
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/ospfroute_down.txt
@@ -0,0 +1,14 @@
+VRF Name: r2-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r2-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r2/zebra.conf b/tests/topotests/ospf_netns_vrf/r2/zebra.conf
new file mode 100644
index 0000000..6ff72d1
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r2
+password zebra
+log file /tmp/r2-zebra.log
+!
+interface r2-eth0 vrf r2-ospf-cust1
+ ip address 10.0.2.1/24
+!
+interface r2-eth1 vrf r2-ospf-cust1
+ ip address 10.0.3.3/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt
new file mode 100644
index 0000000..df66e92
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/zebraroute.txt
@@ -0,0 +1,8 @@
+VRF r2-ospf-cust1:
+O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX
+O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX
+C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX
+O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX
+O>* 10.0.10.0/24 [110/20] via 10.0.3.1, r2-eth1, weight 1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt
new file mode 100644
index 0000000..4afc354
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r2/zebraroutedown.txt
@@ -0,0 +1,7 @@
+VRF r2-ospf-cust1:
+O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r2-eth1, weight 1, XX:XX:XX
+O 10.0.2.0/24 [110/10] is directly connected, r2-eth0, weight 1, XX:XX:XX
+C>* 10.0.2.0/24 is directly connected, r2-eth0, XX:XX:XX
+O 10.0.3.0/24 [110/10] is directly connected, r2-eth1, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r2-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/r3/ospfd.conf b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf
new file mode 100644
index 0000000..b73d547
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/ospfd.conf
@@ -0,0 +1,15 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-ospfd.log
+!
+!
+router ospf vrf r3-ospf-cust1
+ ospf router-id 10.0.255.3
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.3.0/24 area 0
+ network 10.0.10.0/24 area 0
+ network 172.16.0.0/24 area 1
+!
diff --git a/tests/topotests/ospf_netns_vrf/r3/ospfroute.txt b/tests/topotests/ospf_netns_vrf/r3/ospfroute.txt
new file mode 100644
index 0000000..917702b
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/ospfroute.txt
@@ -0,0 +1,18 @@
+VRF Name: r3-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r3-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r3-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth0
+N 10.0.10.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r3-eth0
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r3-eth0
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r3/ospfroute_down.txt b/tests/topotests/ospf_netns_vrf/r3/ospfroute_down.txt
new file mode 100644
index 0000000..966185e
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/ospfroute_down.txt
@@ -0,0 +1,8 @@
+VRF Name: r3-ospf-cust1
+============ OSPF network routing table ============
+N 10.0.10.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+
+============ OSPF router routing table =============
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_netns_vrf/r3/zebra.conf b/tests/topotests/ospf_netns_vrf/r3/zebra.conf
new file mode 100644
index 0000000..1534150
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r3
+password zebra
+log file /tmp/r3-zebra.log
+!
+interface r3-eth0 vrf r3-ospf-cust1
+ ip address 10.0.3.1/24
+!
+interface r3-eth1 vrf r3-ospf-cust1
+ ip address 10.0.10.1/24
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt
new file mode 100644
index 0000000..b435c2e
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/zebraroute.txt
@@ -0,0 +1,8 @@
+VRF r3-ospf-cust1:
+O>* 10.0.1.0/24 [110/20] via 10.0.3.2, r3-eth0, weight 1, XX:XX:XX
+O>* 10.0.2.0/24 [110/20] via 10.0.3.3, r3-eth0, weight 1, XX:XX:XX
+O 10.0.3.0/24 [110/10] is directly connected, r3-eth0, weight 1, XX:XX:XX
+C>* 10.0.3.0/24 is directly connected, r3-eth0, XX:XX:XX
+O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX
+C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt
new file mode 100644
index 0000000..f30a4be
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/r3/zebraroutedown.txt
@@ -0,0 +1,4 @@
+VRF r3-ospf-cust1:
+O 10.0.10.0/24 [110/10] is directly connected, r3-eth1, weight 1, XX:XX:XX
+C>* 10.0.10.0/24 is directly connected, r3-eth1, XX:XX:XX
+
diff --git a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.dot b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.dot
new file mode 100644
index 0000000..789fdd7
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.dot
@@ -0,0 +1,78 @@
+## 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 ospf_topo1 {
+ label="ospf topo1";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.255.1/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ label="r2\nrtr-id 10.0.255.2/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ label="r3\nrtr-id 10.0.255.3/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ label="s1\n10.0.1.0/24\n2001:db8:1::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.2.0/24\n2001:db8:2::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ label="s3\n10.0.3.0/24\n2001:db8:3::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ label="s4\n10.0.10.0/24\n2001:db8:100::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="area 0"
+
+ r1 -- s1 [label="eth0\n.1\n::1"];
+ r1 -- s3 [label="eth1\n.2\n::2"];
+
+ r2 -- s2 [label="eth0\n.1\n::1"];
+ r2 -- s3 [label="eth1\n.3\n::3"];
+
+ r3 -- s3 [label="eth0\n.1\n::1"];
+ r3 -- s4 [label="eth1\n.1\n::1"];
+ }
+
+}
diff --git a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.jpg b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.jpg
new file mode 100644
index 0000000..85f2e52
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.jpg
Binary files differ
diff --git a/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py
new file mode 100644
index 0000000..621d807
--- /dev/null
+++ b/tests/topotests/ospf_netns_vrf/test_ospf_netns_vrf.py
@@ -0,0 +1,324 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_netns_vrf.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_ospf_netns_vrf.py: Test OSPF with Network Namespace VRFs.
+"""
+
+import os
+import sys
+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.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 3 routers
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2 and 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Create empty netowrk for router3
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # check for zebra capability
+ for rname, router in router_list.items():
+ if router.check_capability(TopoRouter.RD_ZEBRA, "--vrfwnetns") == False:
+ return pytest.skip(
+ "Skipping OSPF VRF NETNS feature. VRF NETNS backend not available on FRR"
+ )
+
+ if os.system("ip netns list") != 0:
+ return pytest.skip(
+ "Skipping OSPF VRF NETNS Test. NETNS not available on System"
+ )
+
+ logger.info("Testing with VRF Namespace support")
+
+ for rname, router in router_list.items():
+ # create VRF rx-ospf-cust1 and link rx-eth{0,1} to rx-ospf-cust1
+ ns = "{}-ospf-cust1".format(rname)
+ router.net.add_netns(ns)
+ router.net.set_intf_netns(rname + "-eth0", ns, up=True)
+ router.net.set_intf_netns(rname + "-eth1", ns, up=True)
+
+ router.load_config(
+ TopoRouter.RD_ZEBRA,
+ os.path.join(CWD, "{}/zebra.conf".format(rname)),
+ "--vrfwnetns",
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+ for router in router_list.values():
+ if router.has_version("<", "4.0"):
+ tgen.set_error("unsupported version")
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # Move interfaces out of vrf namespace and delete the namespace
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ tgen.net[rname].reset_intf_netns(rname + "-eth0")
+ tgen.net[rname].reset_intf_netns(rname + "-eth1")
+ tgen.net[rname].delete_netns(rname + "-ospf-cust1")
+ tgen.stop_topology()
+
+
+# Shared test function to validate expected output.
+def compare_show_ip_route_vrf(rname, expected):
+ """
+ Calls 'show ip ospf vrf [rname]-ospf-cust1 route' for router `rname` and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+ vrf_name = "{0}-ospf-cust1".format(rname)
+ current = topotest.ip4_route_zebra(tgen.gears[rname], vrf_name)
+ ret = topotest.difflines(
+ current, expected, title1="Current output", title2="Expected output"
+ )
+ return ret
+
+
+def test_ospf_convergence():
+ "Test OSPF daemon convergence"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rname, router in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', rname)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospfroute.txt".format(rname))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(
+ topotest.router_output_cmp,
+ router,
+ "show ip ospf vrf {0}-ospf-cust1 route".format(rname),
+ expected,
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5)
+ assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_ospf_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name)
+ reffile = os.path.join(CWD, "{}/zebraroute.txt".format(router.name))
+ expected = open(reffile).read()
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ip_route_vrf, router.name, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5)
+ assertmsg = 'OSPF IPv4 route mismatch in router "{}": {}'.format(
+ router.name, diff
+ )
+ assert result, assertmsg
+
+
+def test_ospf_json():
+ "Test 'show ip ospf json' output for coherency."
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rname, router in tgen.routers().items():
+ logger.info(
+ 'Comparing router "%s" "show ip ospf vrf %s-ospf-cust1 json" output',
+ router.name,
+ router.name,
+ )
+ expected = {
+ "{}-ospf-cust1".format(router.name): {
+ "vrfName": "{}-ospf-cust1".format(router.name),
+ "routerId": "10.0.255.{}".format(rname[1:]),
+ "tosRoutesOnly": True,
+ "rfc2328Conform": True,
+ "spfScheduleDelayMsecs": 0,
+ "holdtimeMinMsecs": 50,
+ "holdtimeMaxMsecs": 5000,
+ "lsaMinIntervalMsecs": 5000,
+ "lsaMinArrivalMsecs": 1000,
+ "writeMultiplier": 20,
+ "refreshTimerMsecs": 10000,
+ "asbrRouter": "injectingExternalRoutingInformation",
+ "attachedAreaCounter": 1,
+ "areas": {},
+ }
+ }
+ # Area specific additional checks
+ if router.name == "r1" or router.name == "r2" or router.name == "r3":
+ expected["{}-ospf-cust1".format(router.name)]["areas"]["0.0.0.0"] = {
+ "areaIfActiveCounter": 2,
+ "areaIfTotalCounter": 2,
+ "authentication": "authenticationNone",
+ "backbone": True,
+ "lsaAsbrNumber": 0,
+ "lsaNetworkNumber": 1,
+ "lsaNssaNumber": 0,
+ "lsaNumber": 4,
+ "lsaOpaqueAreaNumber": 0,
+ "lsaOpaqueLinkNumber": 0,
+ "lsaRouterNumber": 3,
+ "lsaSummaryNumber": 0,
+ "nbrFullAdjacentCounter": 2,
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip ospf vrf {0}-ospf-cust1 json".format(rname),
+ expected,
+ )
+ _, diff = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_link_down():
+ "Test OSPF convergence after a link goes down"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Simulate a network down event on router3 switch3 interface.
+ router3 = tgen.gears["r3"]
+ topotest.interface_set_status(
+ router3, "r3-eth0", ifaceaction=False, vrf_name="r3-ospf-cust1"
+ )
+
+ # Expect convergence on all routers
+ for rname, router in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence after link failure', rname)
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(rname))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(
+ topotest.router_output_cmp,
+ router,
+ "show ip ospf vrf {0}-ospf-cust1 route".format(rname),
+ expected,
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5)
+ assertmsg = "OSPF did not converge on {}:\n{}".format(rname, diff)
+ assert result, assertmsg
+
+
+def test_ospf_link_down_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info(
+ 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name
+ )
+
+ str = "{0}-ospf-cust1".format(router.name)
+ reffile = os.path.join(CWD, "{}/zebraroutedown.txt".format(router.name))
+ expected = open(reffile).read()
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ip_route_vrf, router.name, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5)
+ assertmsg = (
+ 'OSPF IPv4 route mismatch in router "{}" after link down: {}'.format(
+ router.name, diff
+ )
+ )
+ assert result, assertmsg
+
+
+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/ospf_sr_te_topo1/dst/zebra.conf b/tests/topotests/ospf_sr_te_topo1/dst/zebra.conf
new file mode 100644
index 0000000..fbe55e5
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/dst/zebra.conf
@@ -0,0 +1,23 @@
+log file zebra.log
+!
+hostname dst
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 9.9.9.2/32
+ ipv6 address 2001:db8:1066::2/128
+!
+interface eth-rt6
+ ip address 10.0.11.2/24
+ link-params
+ enable
+ exit-link-params
+!
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf
new file mode 100644
index 0000000..efc0370
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/bgpd.conf
@@ -0,0 +1,16 @@
+log file bgpd.log
+!
+router bgp 1
+ bgp router-id 1.1.1.1
+ neighbor 6.6.6.6 remote-as 1
+ neighbor 6.6.6.6 update-source lo
+ !
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 6.6.6.6 next-hop-self
+ neighbor 6.6.6.6 route-map SET_SR_POLICY in
+ exit-address-family
+!
+route-map SET_SR_POLICY permit 10
+ set sr-te color 1
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..064a4bf
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/ospfd.conf
@@ -0,0 +1,35 @@
+password 1
+hostname rt1
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-sw1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 1.1.1.1
+ network 1.1.1.1/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ !ospf opaque-lsa
+ mpls-te on
+ mpls-te export
+ mpls-te router-address 1.1.1.1
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf b/tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf
new file mode 100644
index 0000000..55d5857
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/pathd.conf
@@ -0,0 +1,27 @@
+log file pathd.log
+!
+hostname rt1
+!
+segment-routing
+ traffic-eng
+ mpls-te on
+ mpls-te import ospfv2
+ segment-list default
+ index 10 nai adjacency 10.0.1.1 10.0.1.2
+ index 20 nai adjacency 10.0.2.2 10.0.2.4
+ index 30 nai adjacency 10.0.7.4 10.0.7.6
+
+
+ !
+ segment-list test
+ index 10 nai adjacency 10.0.1.1 10.0.1.2
+ index 20 nai adjacency 10.0.2.2 10.0.2.4
+ index 30 nai adjacency 10.0.6.4 10.0.6.5
+ index 40 nai adjacency 10.0.8.5 10.0.8.6
+ !
+ policy color 1 endpoint 6.6.6.6
+ name default
+ binding-sid 1111
+ !
+ !
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref
new file mode 100644
index 0000000..4ef8d94
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000..9b28f6a
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/step2/show_operational_data_with_candidate.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000..9b28f6a
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_single_candidate.ref
@@ -0,0 +1,20 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000..2491171
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/step3/show_operational_data_with_two_candidates.ref
@@ -0,0 +1,25 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "6.6.6.6",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "discriminator": "*",
+ "is-best-candidate-path": false
+ },
+ {
+ "preference": 200,
+ "discriminator": "*",
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..6b282b1
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt1/zebra.conf
@@ -0,0 +1,21 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..65b20f0
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt2/ospfd.conf
@@ -0,0 +1,46 @@
+hostname rt2
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-sw1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 2.2.2.2
+ network 2.2.2.2/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ !ospf opaque-lsa
+ mpls-te on
+ !mpls-te export
+ mpls-te router-address 2.2.2.2
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..24795c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt2/zebra.conf
@@ -0,0 +1,35 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+ link-params
+ enable
+ exit-link-params
+!
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+ link-params
+ enable
+ exit-link-params
+!
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..5be0c49
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt3/ospfd.conf
@@ -0,0 +1,45 @@
+hostname rt3
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-sw1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 3.3.3.3
+ network 3.3.3.3/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ !ospf opaque-lsa
+ mpls-te on
+ !mpls-te export
+ mpls-te router-address 3.3.3.3
+ router-info area 0.0.0.0
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..353c710
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt3/zebra.conf
@@ -0,0 +1,33 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+ link-params
+ enable
+ exit-link-params
+!!
+interface eth-rt5-1
+ ip address 10.0.4.3/24
+ link-params
+ enable
+ exit-link-params
+!!
+interface eth-rt5-2
+ ip address 10.0.5.3/24
+ link-params
+ enable
+ exit-link-params
+!!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..7cdf032
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt4/ospfd.conf
@@ -0,0 +1,52 @@
+hostname rt4
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt2-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt2-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 4.4.4.4
+ network 4.4.4.4/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ !ospf opaque-lsa
+ mpls-te on
+ !mpls-te export
+ mpls-te router-address 4.4.4.4
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..966eb72
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt4/zebra.conf
@@ -0,0 +1,43 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt2-1
+ ip address 10.0.2.4/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt2-2
+ ip address 10.0.3.4/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000..8f71cda
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt5/ospfd.conf
@@ -0,0 +1,52 @@
+hostname rt5
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt3-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt3-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 5.5.5.5
+ network 5.5.5.5/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+! ospf opaque-lsa
+ mpls-te on
+! mpls-te export
+ mpls-te router-address 5.5.5.5
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..3184f1d
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt5/zebra.conf
@@ -0,0 +1,43 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt3-1
+ ip address 10.0.4.5/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt3-2
+ ip address 10.0.5.5/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf
new file mode 100644
index 0000000..e72ee52
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/bgpd.conf
@@ -0,0 +1,12 @@
+log file bgpd.log
+!
+router bgp 1
+ bgp router-id 6.6.6.6
+ neighbor 1.1.1.1 remote-as 1
+ neighbor 1.1.1.1 update-source lo
+ !
+ address-family ipv4 unicast
+ redistribute static
+ neighbor 1.1.1.1 next-hop-self
+ exit-address-family
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf
new file mode 100644
index 0000000..20c8975
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/ospfd.conf
@@ -0,0 +1,40 @@
+hostname rt6
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 6.6.6.6
+ network 6.6.6.6/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+! ospf opaque-lsa
+ mpls-te on
+ mpls-te export
+ mpls-te router-address 6.6.6.6
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf b/tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf
new file mode 100644
index 0000000..696df22
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/pathd.conf
@@ -0,0 +1,25 @@
+log file pathd.log
+!
+hostname rt6
+!
+segment-routing
+ traffic-eng
+ mpls-te on
+ mpls-te import ospfv2
+ segment-list default
+ index 10 nai adjacency 10.0.7.6 10.0.7.4
+ index 20 nai adjacency 10.0.2.4 10.0.2.2
+ index 30 nai adjacency 10.0.1.2 10.0.1.1
+ !
+ segment-list test
+ index 10 nai adjacency 10.0.8.6 10.0.8.5
+ index 20 nai adjacency 10.0.6.5 10.0.6.4
+ index 30 nai adjacency 10.0.2.4 10.0.2.2
+ index 40 nai adjacency 10.0.1.2 10.0.1.1
+ !
+ policy color 1 endpoint 1.1.1.1
+ name default
+ binding-sid 6666
+ !
+ !
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref
new file mode 100644
index 0000000..241c80b
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data.ref
@@ -0,0 +1,13 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": false
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref
new file mode 100644
index 0000000..20ea69e
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/step2/show_operational_data_with_candidate.ref
@@ -0,0 +1,19 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref
new file mode 100644
index 0000000..20ea69e
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_single_candidate.ref
@@ -0,0 +1,19 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref
new file mode 100644
index 0000000..10cafe9
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/step3/show_operational_data_with_two_candidates.ref
@@ -0,0 +1,23 @@
+{
+ "frr-pathd:pathd": {
+ "srte": {
+ "policy": [
+ {
+ "color": 1,
+ "endpoint": "1.1.1.1",
+ "is-operational": true,
+ "candidate-path": [
+ {
+ "preference": 100,
+ "is-best-candidate-path": false
+ },
+ {
+ "preference": 200,
+ "is-best-candidate-path": true
+ }
+ ]
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf b/tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..c556aa3
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/rt6/zebra.conf
@@ -0,0 +1,38 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+interface eth-dst
+ ip address 10.0.11.1/24
+ link-params
+ enable
+ exit-link-params
+!!
+!
+ip forwarding
+!
+ip route 9.9.9.2/32 10.0.11.2
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py b/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py
new file mode 100755
index 0000000..6e99267
--- /dev/null
+++ b/tests/topotests/ospf_sr_te_topo1/test_ospf_sr_te_topo1.py
@@ -0,0 +1,666 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_sr_te_topo1.py
+#
+# Copyright (c) 2021 by
+# Volta Networks
+#
+# 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_ospf_sr_te_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+ + 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| eth-rt5-1| |eth-rt5-2
+ | | |
+ 10.0.2.0/24| 10.0.4.0/24| |10.0.5.0/24
+ | | |
+ eth-rt2-1| eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+ |eth-dst (.1)
+ |
+ |10.0.11.0/24
+ |
+ |eth-rt6 (.2)
+ +---------+
+ | |
+ | DST |
+ | 9.9.9.2 |
+ | |
+ +---------+
+
+"""
+
+import os
+import sys
+import pytest
+import json
+from time import sleep
+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.
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.pathd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6", "dst"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ # switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+
+ # switch = tgen.add_switch("s3")
+ # switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+ # switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+ switch = tgen.add_switch("s9")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-dst")
+ switch.add_link(tgen.gears["dst"], nodeif="eth-rt6")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+
+ frrdir = tgen.config.get(tgen.CONFIG_SECTION, "frrdir")
+ if not os.path.isfile(os.path.join(frrdir, "pathd")):
+ pytest.skip("pathd daemon wasn't built in:" + frrdir)
+
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PATH, os.path.join(CWD, "{}/pathd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ 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 setup_testcase(msg):
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+def print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def compare_json_test(router, command, reference, exact):
+ output = router.vtysh_cmd(command, isjson=True)
+ result = topotest.json_cmp(output, reference)
+
+ # Note: topotest.json_cmp() just checks on inclusion of keys.
+ # For exact matching also compare the other way around.
+ if not result and exact:
+ return topotest.json_cmp(reference, output)
+ else:
+ return result
+
+
+def cmp_json_output(rname, command, reference, exact=False):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_json_test, tgen.gears[rname], command, expected, exact)
+ _, diff = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def cmp_json_output_exact(rname, command, reference):
+ return cmp_json_output(rname, command, reference, True)
+
+
+def add_candidate_path(rname, endpoint, pref, name, segment_list="default"):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color 1 endpoint """
+ + endpoint
+ + """" \
+ -c "candidate-path preference """
+ + str(pref)
+ + """ name """
+ + name
+ + """ explicit segment-list """
+ + segment_list
+ + '''"'''
+ )
+
+
+def delete_candidate_path(rname, endpoint, pref):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color 1 endpoint """
+ + endpoint
+ + """" \
+ -c "no candidate-path preference """
+ + str(pref)
+ + '''"'''
+ )
+
+
+def add_segment(rname, name, index, label):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "segment-list """
+ + name
+ + """" \
+ -c "index """
+ + str(index)
+ + """ mpls label """
+ + str(label)
+ + '''"'''
+ )
+
+
+def delete_segment(rname, name, index):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "segment-list """
+ + name
+ + """" \
+ -c "no index """
+ + str(index)
+ + '''"'''
+ )
+
+
+def add_segment_adj(rname, name, index, src, dst):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "segment-list """
+ + name
+ + """" \
+ -c "index """
+ + str(index)
+ + """ nai adjacency """
+ + str(src)
+ + """ """
+ + str(dst)
+ + '''"'''
+ )
+
+
+def create_sr_policy(rname, endpoint, bsid):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "policy color 1 endpoint """
+ + endpoint
+ + """" \
+ -c "name default" \
+ -c "binding-sid """
+ + str(bsid)
+ + '''"'''
+ )
+
+
+def delete_sr_policy(rname, endpoint):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "segment-routing" \
+ -c "traffic-eng" \
+ -c "no policy color 1 endpoint """
+ + endpoint
+ + '''"'''
+ )
+
+
+def create_prefix_sid(rname, prefix, sid):
+ get_topogen().net[rname].cmd(
+ """ \
+ vtysh -c "conf t" \
+ -c "router ospf " \
+ -c "segment-routing prefix """
+ + prefix
+ + " index "
+ + str(sid)
+ + '''"'''
+ )
+
+
+def delete_prefix_sid(rname, prefix):
+ get_topogen().net[rname].cmd(
+ ''' \
+ vtysh -c "conf t" \
+ -c "router ospf " \
+ -c "no segment-routing prefix "'''
+ + prefix
+ )
+
+
+def check_bsid(rt, bsid, fn_name, positive):
+ """
+ Search for a bsid in rt1 and rt6
+ Positive means that check is true is bsid is found
+ Positive="False" means that check is true is bsid is NOT found
+ """
+
+ logger.info('Checking "%s" bsid "%s" for router "%s" ', positive, bsid, rt)
+
+ count = 0
+ candidate_key = bsid
+ candidate_output = ""
+ # First wait for convergence
+ tgen = get_topogen()
+ while count < 30:
+ matched = False
+ matched_key = False
+ sleep(1)
+ count += 1
+ router = tgen.gears[rt]
+ candidate_output = router.vtysh_cmd("show mpls table json")
+ candidate_output_json = json.loads(candidate_output)
+ for item in candidate_output_json.items():
+ # logger.info('item "%s"', item)
+ if item[0] == candidate_key:
+ matched_key = True
+ if positive:
+ break
+ if positive:
+ if matched_key:
+ matched = True
+ assertmsg = "{} don't has entry {} but is was expected".format(
+ router.name, candidate_key
+ )
+ else:
+ if not matched_key:
+ matched = True
+ assertmsg = "{} has entry {} but is wans't expected".format(
+ router.name, candidate_key
+ )
+ if matched:
+ logger.info('Success "%s" in "%s"', router.name, fn_name)
+ return
+ assert matched, assertmsg
+
+
+#
+# Step 1
+#
+# Checking the MPLS table using a single SR Policy and a single Candidate Path
+# Segment list are base in adjacency that query TED
+#
+def test_srte_init_step1():
+ setup_testcase("Test (step 1): wait OSPF convergence / label distribution")
+
+ check_bsid("rt1", "1111", test_srte_init_step1.__name__, False)
+ check_bsid("rt6", "6666", test_srte_init_step1.__name__, False)
+
+
+def test_srte_add_candidate_check_mpls_table_step1():
+ setup_testcase("Test (step 1): check MPLS table regarding the added Candidate Path")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+def test_srte_reinstall_sr_policy_check_mpls_table_step1():
+ setup_testcase(
+ "Test (step 1): check MPLS table after the SR Policy was removed and reinstalled"
+ )
+
+ for rname, endpoint, bsid in [("rt1", "6.6.6.6", 1111), ("rt6", "1.1.1.1", 6666)]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ delete_sr_policy(rname, endpoint)
+ check_bsid(rname, bsid, test_srte_init_step1.__name__, False)
+ create_sr_policy(rname, endpoint, bsid)
+ add_candidate_path(rname, endpoint, 100, "default")
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+#
+# Step 2
+#
+# Checking pathd operational data using a single SR Policy and a single Candidate Path
+# Segment list are base in adjacency that query TED
+#
+def test_srte_bare_policy_step2():
+ setup_testcase("Test (step 2): bare SR Policy should not be operational")
+
+ for rname in ["rt1", "rt6"]:
+ cmp_json_output_exact(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data.ref",
+ )
+
+
+def test_srte_add_candidate_check_operational_data_step2():
+ setup_testcase(
+ "Test (step 2): add single Candidate Path, SR Policy should be operational"
+ )
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data_with_candidate.ref",
+ )
+
+
+def test_srte_config_remove_candidate_check_operational_data_step2():
+ setup_testcase(
+ "Test (step 2): remove single Candidate Path, SR Policy should not be operational anymore"
+ )
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ delete_candidate_path(rname, endpoint, 100)
+ cmp_json_output_exact(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step2/show_operational_data.ref",
+ )
+
+
+#
+# Step 3
+#
+# Testing the Candidate Path selection
+# Segment list are based in adjacencies resolved by query TED
+#
+def test_srte_add_two_candidates_step3():
+ setup_testcase("Test (step 3): second Candidate Path has higher Priority")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("100", "first"), ("200", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_two_candidates.ref",
+ )
+
+ # cleanup
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref in ["100", "200"]:
+ delete_candidate_path(rname, endpoint, pref)
+
+
+def test_srte_add_two_candidates_with_reverse_priority_step3():
+ setup_testcase("Test (step 3): second Candidate Path has lower Priority")
+
+ # Use reversed priorities here
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("200", "first"), ("100", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_two_candidates.ref",
+ )
+
+ # cleanup
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref in ["100", "200"]:
+ delete_candidate_path(rname, endpoint, pref)
+
+
+def test_srte_remove_best_candidate_step3():
+ setup_testcase("Test (step 3): delete the Candidate Path with higher priority")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ for pref, cand_name in [("100", "first"), ("200", "second")]:
+ add_candidate_path(rname, endpoint, pref, cand_name)
+
+ # Delete candidate with higher priority
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ delete_candidate_path(rname, endpoint, 200)
+
+ # Candidate with lower priority should get active now
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ cmp_json_output(
+ rname,
+ "show yang operational-data /frr-pathd:pathd pathd",
+ "step3/show_operational_data_with_single_candidate.ref",
+ )
+ # cleanup
+ delete_candidate_path(rname, endpoint, 100)
+
+
+#
+# Step 4
+#
+# Checking MPLS table with a single SR Policy and a Candidate Path with different Segment Lists and other modifications
+# Segment list are base in adjacency that query TED
+#
+def test_srte_change_segment_list_check_mpls_table_step4():
+ setup_testcase("Test (step 4): check MPLS table for changed Segment List")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ # now change the segment list name
+ add_candidate_path(rname, endpoint, 100, "default", "test")
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_segment(rname, "test", 10)
+ delete_segment(rname, "test", 20)
+ delete_segment(rname, "test", 30)
+ delete_segment(rname, "test", 40)
+ if rname == "rt1":
+ add_segment_adj(rname, "test", 10, "10.0.1.1", "10.0.1.2")
+ add_segment_adj(rname, "test", 20, "10.0.2.2", "10.0.2.4")
+ add_segment_adj(rname, "test", 30, "10.0.6.4", "10.0.6.5")
+ add_segment_adj(rname, "test", 40, "10.0.8.5", "10.0.8.6")
+ else:
+ add_segment_adj(rname, "test", 10, "10.0.8.6", "10.0.8.5")
+ add_segment_adj(rname, "test", 20, "10.0.6.5", "10.0.6.4")
+ add_segment_adj(rname, "test", 30, "10.0.2.4", "10.0.2.2")
+ add_segment_adj(rname, "test", 40, "10.0.1.2", "10.0.1.1")
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+def test_srte_change_sl_priority_error_ted_check_mpls_table_step4():
+ setup_testcase("Test (step 4): check MPLS table keeps low prio sl")
+
+ for rname, endpoint in [("rt1", "6.6.6.6"), ("rt6", "1.1.1.1")]:
+ add_candidate_path(rname, endpoint, 100, "default")
+ # now change the segment list name
+ add_candidate_path(rname, endpoint, 200, "test", "test")
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_segment(rname, "test", 10)
+ delete_segment(rname, "test", 20)
+ delete_segment(rname, "test", 30)
+ delete_segment(rname, "test", 40)
+ # These won't resolv
+ if rname == "rt1":
+ add_segment_adj(rname, "test", 10, "10.0.1.99", "10.0.1.99")
+ add_segment_adj(rname, "test", 20, "10.0.2.99", "10.0.2.99")
+ add_segment_adj(rname, "test", 30, "10.0.6.99", "10.0.6.99")
+ add_segment_adj(rname, "test", 40, "10.0.8.99", "10.0.8.99")
+ else:
+ add_segment_adj(rname, "test", 10, "10.0.8.99", "10.0.8.99")
+ add_segment_adj(rname, "test", 20, "10.0.6.99", "10.0.6.99")
+ add_segment_adj(rname, "test", 30, "10.0.2.99", "10.0.2.99")
+ add_segment_adj(rname, "test", 40, "10.0.1.99", "10.0.1.99")
+ # So policy sticks with default sl even higher prio
+ check_bsid(
+ rname,
+ "1111" if rname == "rt1" else "6666",
+ test_srte_init_step1.__name__,
+ True,
+ )
+ delete_candidate_path(rname, endpoint, 100)
+
+
+# Memory leak test template
+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/ospf_sr_topo1/__init__.py b/tests/topotests/ospf_sr_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/__init__.py
diff --git a/tests/topotests/ospf_sr_topo1/rt1/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..be9abf6
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/ospfd.conf
@@ -0,0 +1,31 @@
+password 1
+hostname rt1
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-sw1
+ ip ospf network broadcast
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 1.1.1.1
+ network 1.1.1.1/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.1
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step1/show_ip_route.ref
new file mode 100644
index 0000000..374184e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step1/show_ip_route.ref
@@ -0,0 +1,289 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step1/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step1/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step10/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step10/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step10/show_mpls_table.ref
new file mode 100644
index 0000000..2006392
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step10/show_mpls_table.ref
@@ -0,0 +1,79 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step2/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step2/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step2/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step2/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step3/show_ip_route.ref
new file mode 100644
index 0000000..f6ead5c
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step3/show_ip_route.ref
@@ -0,0 +1,249 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step3/show_mpls_table.ref
new file mode 100644
index 0000000..96e0591
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step3/show_mpls_table.ref
@@ -0,0 +1,50 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step4/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step4/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step4/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step4/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step5/show_ip_route.ref
new file mode 100644
index 0000000..f2b8924
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step5/show_ip_route.ref
@@ -0,0 +1,276 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step5/show_mpls_table.ref
new file mode 100644
index 0000000..96e0591
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step5/show_mpls_table.ref
@@ -0,0 +1,50 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step6/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step6/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step6/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step6/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step7/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step7/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step7/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step7/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step8/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step8/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step8/show_mpls_table.ref
new file mode 100644
index 0000000..de906c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step8/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt1/step9/show_ip_route.ref
new file mode 100644
index 0000000..37f7362
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step9/show_ip_route.ref
@@ -0,0 +1,282 @@
+{
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt1/step9/show_mpls_table.ref
new file mode 100644
index 0000000..2006392
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/step9/show_mpls_table.ref
@@ -0,0 +1,79 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17060,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt1/zebra.conf b/tests/topotests/ospf_sr_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..520f2e4
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt1/zebra.conf
@@ -0,0 +1,18 @@
+log file zebra.log
+!
+hostname rt1
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-sw1
+ ip address 10.0.1.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt2/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..30ef12a
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/ospfd.conf
@@ -0,0 +1,41 @@
+password 1
+hostname rt2
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-sw1
+ ip ospf network broadcast
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt4-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt4-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 2.2.2.2
+ network 2.2.2.2/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 2.2.2.2
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 2.2.2.2/32 index 20 no-php-flag
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step1/show_ip_route.ref
new file mode 100644
index 0000000..3dde042
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step1/show_ip_route.ref
@@ -0,0 +1,330 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step1/show_mpls_table.ref
new file mode 100644
index 0000000..eba7c40
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step1/show_mpls_table.ref
@@ -0,0 +1,97 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step10/show_ip_route.ref
new file mode 100644
index 0000000..9a06059
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step10/show_ip_route.ref
@@ -0,0 +1,254 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step10/show_mpls_table.ref
new file mode 100644
index 0000000..be44a75
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step10/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step2/show_ip_route.ref
new file mode 100644
index 0000000..384aac0
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step2/show_ip_route.ref
@@ -0,0 +1,303 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step2/show_mpls_table.ref
new file mode 100644
index 0000000..5088aa2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step2/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step3/show_ip_route.ref
new file mode 100644
index 0000000..879cd1e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step3/show_ip_route.ref
@@ -0,0 +1,256 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step3/show_mpls_table.ref
new file mode 100644
index 0000000..6333e7f
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step3/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step4/show_ip_route.ref
new file mode 100644
index 0000000..384aac0
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step4/show_ip_route.ref
@@ -0,0 +1,303 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step4/show_mpls_table.ref
new file mode 100644
index 0000000..5088aa2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step4/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step5/show_ip_route.ref
new file mode 100644
index 0000000..07edd42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step5/show_ip_route.ref
@@ -0,0 +1,297 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step5/show_mpls_table.ref
new file mode 100644
index 0000000..6333e7f
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step5/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step6/show_ip_route.ref
new file mode 100644
index 0000000..384aac0
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step6/show_ip_route.ref
@@ -0,0 +1,303 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step6/show_mpls_table.ref
new file mode 100644
index 0000000..5088aa2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step6/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step7/show_ip_route.ref
new file mode 100644
index 0000000..274931b
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step7/show_ip_route.ref
@@ -0,0 +1,300 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step7/show_mpls_table.ref
new file mode 100644
index 0000000..cd23725
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step7/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step8/show_ip_route.ref
new file mode 100644
index 0000000..384aac0
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step8/show_ip_route.ref
@@ -0,0 +1,303 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step8/show_mpls_table.ref
new file mode 100644
index 0000000..5088aa2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step8/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt2/step9/show_ip_route.ref
new file mode 100644
index 0000000..c71515f
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step9/show_ip_route.ref
@@ -0,0 +1,303 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 17050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.2.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt2/step9/show_mpls_table.ref
new file mode 100644
index 0000000..2f06641
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/step9/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17050,
+ "installed":true,
+ "nexthop":"10.0.1.3"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.2.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.3.4"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt2/zebra.conf b/tests/topotests/ospf_sr_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..fbf805c
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt2/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname rt2
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 2.2.2.2/32
+!
+interface eth-sw1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4-1
+ ip address 10.0.2.2/24
+!
+interface eth-rt4-2
+ ip address 10.0.3.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt3/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..e315679
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/ospfd.conf
@@ -0,0 +1,41 @@
+password 1
+hostname rt3
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-sw1
+ ip ospf network broadcast
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt5-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt5-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 3.3.3.3
+ network 3.3.3.3/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 3.3.3.3
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 17000 24999
+ segment-routing node-msd 8
+ segment-routing prefix 3.3.3.3/32 index 30 no-php-flag
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step1/show_ip_route.ref
new file mode 100644
index 0000000..4b1500e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step1/show_ip_route.ref
@@ -0,0 +1,330 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step1/show_mpls_table.ref
new file mode 100644
index 0000000..39cc3e8
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step1/show_mpls_table.ref
@@ -0,0 +1,97 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step10/show_ip_route.ref
new file mode 100644
index 0000000..14a2ac1
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step10/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step10/show_mpls_table.ref
new file mode 100644
index 0000000..a0f7c79
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step10/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step2/show_ip_route.ref
new file mode 100644
index 0000000..63c6a18
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step2/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step2/show_mpls_table.ref
new file mode 100644
index 0000000..1ab2242
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step2/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step3/show_ip_route.ref
new file mode 100644
index 0000000..0894c51
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step3/show_ip_route.ref
@@ -0,0 +1,263 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step3/show_mpls_table.ref
new file mode 100644
index 0000000..4dcaede
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step3/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step4/show_ip_route.ref
new file mode 100644
index 0000000..63c6a18
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step4/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step4/show_mpls_table.ref
new file mode 100644
index 0000000..1ab2242
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step4/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step5/show_ip_route.ref
new file mode 100644
index 0000000..3e74ff0
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step5/show_ip_route.ref
@@ -0,0 +1,304 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step5/show_mpls_table.ref
new file mode 100644
index 0000000..4dcaede
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step5/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step6/show_ip_route.ref
new file mode 100644
index 0000000..63c6a18
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step6/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step6/show_mpls_table.ref
new file mode 100644
index 0000000..1ab2242
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step6/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step7/show_ip_route.ref
new file mode 100644
index 0000000..41544d4
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step7/show_ip_route.ref
@@ -0,0 +1,307 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step7/show_mpls_table.ref
new file mode 100644
index 0000000..bf055ba
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step7/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step8/show_ip_route.ref
new file mode 100644
index 0000000..63c6a18
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step8/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step8/show_mpls_table.ref
new file mode 100644
index 0000000..1ab2242
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step8/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt3/step9/show_ip_route.ref
new file mode 100644
index 0000000..14a2ac1
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step9/show_ip_route.ref
@@ -0,0 +1,310 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.1",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true,
+ "labels":[
+ 16060
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-sw1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt3/step9/show_mpls_table.ref
new file mode 100644
index 0000000..a0f7c79
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/step9/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "17010":{
+ "inLabel":17010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.1.1"
+ }
+ ]
+ },
+ "17020":{
+ "inLabel":17020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17030":{
+ "inLabel":17030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "17040":{
+ "inLabel":17040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.1.2"
+ }
+ ]
+ },
+ "17050":{
+ "inLabel":17050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ },
+ "17060":{
+ "inLabel":17060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.5.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16060,
+ "installed":true,
+ "nexthop":"10.0.4.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt3/zebra.conf b/tests/topotests/ospf_sr_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..fc9db06
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt3/zebra.conf
@@ -0,0 +1,24 @@
+log file zebra.log
+!
+hostname rt3
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 3.3.3.3/32
+!
+interface eth-sw1
+ ip address 10.0.1.3/24
+!
+interface eth-rt5-1
+ ip address 10.0.4.3/24
+!
+interface eth-rt5-2
+ ip address 10.0.5.3/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt4/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..681aaa3
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/ospfd.conf
@@ -0,0 +1,46 @@
+password 1
+hostname rt4
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-rt2-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt2-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 4.4.4.4
+ network 4.4.4.4/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 4.4.4.4
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 4.4.4.4/32 index 40 no-php-flag
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step1/show_ip_route.ref
new file mode 100644
index 0000000..4a2d3aa
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step1/show_ip_route.ref
@@ -0,0 +1,311 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step1/show_mpls_table.ref
new file mode 100644
index 0000000..3246d22
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step1/show_mpls_table.ref
@@ -0,0 +1,97 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.6.5"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step10/show_ip_route.ref
new file mode 100644
index 0000000..db4cf5b
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step10/show_ip_route.ref
@@ -0,0 +1,278 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step10/show_mpls_table.ref
new file mode 100644
index 0000000..58cf526
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step10/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step2/show_ip_route.ref
new file mode 100644
index 0000000..c44b3ee
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step2/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step2/show_mpls_table.ref
new file mode 100644
index 0000000..05f9f28
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step2/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step3/show_ip_route.ref
new file mode 100644
index 0000000..a078dd2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step3/show_ip_route.ref
@@ -0,0 +1,296 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":40,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step3/show_mpls_table.ref
new file mode 100644
index 0000000..f551563
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step3/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step4/show_ip_route.ref
new file mode 100644
index 0000000..b63812a
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step4/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step4/show_mpls_table.ref
new file mode 100644
index 0000000..f2e56c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step4/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step5/show_ip_route.ref
new file mode 100644
index 0000000..3157ae1
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step5/show_ip_route.ref
@@ -0,0 +1,318 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step5/show_mpls_table.ref
new file mode 100644
index 0000000..8213840
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step5/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step6/show_ip_route.ref
new file mode 100644
index 0000000..b63812a
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step6/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step6/show_mpls_table.ref
new file mode 100644
index 0000000..f2e56c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step6/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step7/show_ip_route.ref
new file mode 100644
index 0000000..775d8c4
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step7/show_ip_route.ref
@@ -0,0 +1,318 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step7/show_mpls_table.ref
new file mode 100644
index 0000000..8a5fdef
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step7/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step8/show_ip_route.ref
new file mode 100644
index 0000000..b63812a
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step8/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step8/show_mpls_table.ref
new file mode 100644
index 0000000..f2e56c2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step8/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt4/step9/show_ip_route.ref
new file mode 100644
index 0000000..48e306d
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step9/show_ip_route.ref
@@ -0,0 +1,324 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18050
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.2.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt2-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":30,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt4/step9/show_mpls_table.ref
new file mode 100644
index 0000000..275abab
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/step9/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.2.2"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.3.2"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18050,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.7.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt4/zebra.conf b/tests/topotests/ospf_sr_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..d794837
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt4/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname rt4
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 4.4.4.4/32
+!
+interface eth-rt2-1
+ ip address 10.0.2.4/24
+!
+interface eth-rt2-2
+ ip address 10.0.3.4/24
+!
+interface eth-rt5
+ ip address 10.0.6.4/24
+!
+interface eth-rt6
+ ip address 10.0.7.4/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt5/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000..0b441c7
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/ospfd.conf
@@ -0,0 +1,46 @@
+password 1
+hostname rt5
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-rt3-1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt3-2
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt6
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 5.5.5.5
+ network 5.5.5.5/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 5.5.5.5
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 5.5.5.5/32 index 50 no-php-flag
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step1/show_ip_route.ref
new file mode 100644
index 0000000..0a43788
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step1/show_ip_route.ref
@@ -0,0 +1,311 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.6.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step1/show_mpls_table.ref
new file mode 100644
index 0000000..e8c4608
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step1/show_mpls_table.ref
@@ -0,0 +1,97 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.6.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step10/show_ip_route.ref
new file mode 100644
index 0000000..2bad2eb
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step10/show_ip_route.ref
@@ -0,0 +1,300 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step10/show_mpls_table.ref
new file mode 100644
index 0000000..c5ed18d
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step10/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step2/show_ip_route.ref
new file mode 100644
index 0000000..3572ec7
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step2/show_ip_route.ref
@@ -0,0 +1,307 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step2/show_mpls_table.ref
new file mode 100644
index 0000000..d9cadeb
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step2/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step3/show_ip_route.ref
new file mode 100644
index 0000000..2f7b0cc
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step3/show_ip_route.ref
@@ -0,0 +1,272 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17040
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":40,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step3/show_mpls_table.ref
new file mode 100644
index 0000000..7c78d2c
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step3/show_mpls_table.ref
@@ -0,0 +1,85 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17040,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17040,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step4/show_ip_route.ref
new file mode 100644
index 0000000..1a12715
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step4/show_ip_route.ref
@@ -0,0 +1,307 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step4/show_mpls_table.ref
new file mode 100644
index 0000000..42e476e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step4/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step5/show_ip_route.ref
new file mode 100644
index 0000000..e50fa10
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step5/show_ip_route.ref
@@ -0,0 +1,286 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step5/show_mpls_table.ref
new file mode 100644
index 0000000..bb95379
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step5/show_mpls_table.ref
@@ -0,0 +1,67 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step6/show_ip_route.ref
new file mode 100644
index 0000000..1a12715
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step6/show_ip_route.ref
@@ -0,0 +1,307 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step6/show_mpls_table.ref
new file mode 100644
index 0000000..42e476e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step6/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step7/show_ip_route.ref
new file mode 100644
index 0000000..15a024d
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step7/show_ip_route.ref
@@ -0,0 +1,286 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step7/show_mpls_table.ref
new file mode 100644
index 0000000..cff0d25
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step7/show_mpls_table.ref
@@ -0,0 +1,73 @@
+{
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step8/show_ip_route.ref
new file mode 100644
index 0000000..1a12715
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step8/show_ip_route.ref
@@ -0,0 +1,307 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 0
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step8/show_mpls_table.ref
new file mode 100644
index 0000000..42e476e
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step8/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":0,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt5/step9/show_ip_route.ref
new file mode 100644
index 0000000..d9ddad2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step9/show_ip_route.ref
@@ -0,0 +1,292 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true,
+ "labels":[
+ 17030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 18040
+ ]
+ }
+ ]
+ }
+ ],
+ "6.6.6.6\/32":[
+ {
+ "prefix":"6.6.6.6\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true,
+ "labels":[
+ 3
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.4.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.5.3",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3-2",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.6",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt6",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt5/step9/show_mpls_table.ref
new file mode 100644
index 0000000..c5ed18d
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/step9/show_mpls_table.ref
@@ -0,0 +1,91 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17010,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17020,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.5.3"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":17030,
+ "installed":true,
+ "nexthop":"10.0.4.3"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":18040,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true
+ }
+ ]
+ },
+ "16060":{
+ "inLabel":16060,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":3,
+ "installed":true,
+ "nexthop":"10.0.8.6"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt5/zebra.conf b/tests/topotests/ospf_sr_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..09923f2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt5/zebra.conf
@@ -0,0 +1,27 @@
+log file zebra.log
+!
+hostname rt5
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 5.5.5.5/32
+!
+interface eth-rt3-1
+ ip address 10.0.4.5/24
+!
+interface eth-rt3-2
+ ip address 10.0.5.5/24
+!
+interface eth-rt4
+ ip address 10.0.6.5/24
+!
+interface eth-rt6
+ ip address 10.0.8.5/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt6/ospfd.conf b/tests/topotests/ospf_sr_topo1/rt6/ospfd.conf
new file mode 100644
index 0000000..7bb5de9
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/ospfd.conf
@@ -0,0 +1,36 @@
+password 1
+hostname rt6
+log file ospfd.log
+!
+! debug ospf sr
+! debug ospf te
+! debug ospf event
+! debug ospf lsa
+! debug ospf zebra
+!
+interface lo
+!
+interface eth-rt4
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface eth-rt5
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 6.6.6.6
+ network 6.6.6.6/32 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ capability opaque
+ mpls-te on
+ mpls-te router-address 6.6.6.6
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 6.6.6.6/32 index 60 explicit-null
+!
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step1/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step1/show_ip_route.ref
new file mode 100644
index 0000000..9f05ec7
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step1/show_ip_route.ref
@@ -0,0 +1,291 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step1/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step1/show_mpls_table.ref
new file mode 100644
index 0000000..baa2314
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step1/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step10/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step10/show_ip_route.ref
new file mode 100644
index 0000000..6abb380
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step10/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step10/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step10/show_mpls_table.ref
new file mode 100644
index 0000000..09ecec2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step10/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step2/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step2/show_ip_route.ref
new file mode 100644
index 0000000..80b3c42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step2/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step2/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step2/show_mpls_table.ref
new file mode 100644
index 0000000..baa2314
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step2/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "16010":{
+ "inLabel":16010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16020":{
+ "inLabel":16020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16030":{
+ "inLabel":16030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "16040":{
+ "inLabel":16040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "16050":{
+ "inLabel":16050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step3/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step3/show_ip_route.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step3/show_ip_route.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step3/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step3/show_mpls_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step3/show_mpls_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step4/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step4/show_ip_route.ref
new file mode 100644
index 0000000..80b3c42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step4/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step4/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step4/show_mpls_table.ref
new file mode 100644
index 0000000..09ecec2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step4/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step5/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step5/show_ip_route.ref
new file mode 100644
index 0000000..0e4b3eb
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step5/show_ip_route.ref
@@ -0,0 +1,266 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step5/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step5/show_mpls_table.ref
new file mode 100644
index 0000000..2c63c08
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step5/show_mpls_table.ref
@@ -0,0 +1,2 @@
+{
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step6/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step6/show_ip_route.ref
new file mode 100644
index 0000000..80b3c42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step6/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step6/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step6/show_mpls_table.ref
new file mode 100644
index 0000000..09ecec2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step6/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step7/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step7/show_ip_route.ref
new file mode 100644
index 0000000..aa2329a
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step7/show_ip_route.ref
@@ -0,0 +1,278 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step7/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step7/show_mpls_table.ref
new file mode 100644
index 0000000..800b1ae
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step7/show_mpls_table.ref
@@ -0,0 +1,50 @@
+{
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step8/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step8/show_ip_route.ref
new file mode 100644
index 0000000..80b3c42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step8/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step8/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step8/show_mpls_table.ref
new file mode 100644
index 0000000..09ecec2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step8/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step9/show_ip_route.ref b/tests/topotests/ospf_sr_topo1/rt6/step9/show_ip_route.ref
new file mode 100644
index 0000000..80b3c42
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step9/show_ip_route.ref
@@ -0,0 +1,284 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16010
+ ]
+ }
+ ]
+ }
+ ],
+ "2.2.2.2\/32":[
+ {
+ "prefix":"2.2.2.2\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16020
+ ]
+ }
+ ]
+ }
+ ],
+ "3.3.3.3\/32":[
+ {
+ "prefix":"3.3.3.3\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16030
+ ]
+ }
+ ]
+ }
+ ],
+ "4.4.4.4\/32":[
+ {
+ "prefix":"4.4.4.4\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true,
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "5.5.5.5\/32":[
+ {
+ "prefix":"5.5.5.5\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":10,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true,
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":30,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ },
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.7.4",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.6.0\/24":[
+ {
+ "prefix":"10.0.6.0\/24",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.8.5",
+ "afi":"ipv4",
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.7.0\/24":[
+ {
+ "prefix":"10.0.7.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt4",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.8.0\/24":[
+ {
+ "prefix":"10.0.8.0\/24",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt5",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/step9/show_mpls_table.ref b/tests/topotests/ospf_sr_topo1/rt6/step9/show_mpls_table.ref
new file mode 100644
index 0000000..09ecec2
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/step9/show_mpls_table.ref
@@ -0,0 +1,68 @@
+{
+ "18010":{
+ "inLabel":18010,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ },
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16010,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18020":{
+ "inLabel":18020,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16020,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18030":{
+ "inLabel":18030,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16030,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ },
+ "18040":{
+ "inLabel":18040,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16040,
+ "installed":true,
+ "nexthop":"10.0.7.4"
+ }
+ ]
+ },
+ "18050":{
+ "inLabel":18050,
+ "installed":true,
+ "nexthops":[
+ {
+ "type":"SR (OSPF)",
+ "outLabel":16050,
+ "installed":true,
+ "nexthop":"10.0.8.5"
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_sr_topo1/rt6/zebra.conf b/tests/topotests/ospf_sr_topo1/rt6/zebra.conf
new file mode 100644
index 0000000..1452560
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/rt6/zebra.conf
@@ -0,0 +1,21 @@
+log file zebra.log
+!
+hostname rt6
+!
+! debug zebra kernel
+! debug zebra packet
+! debug zebra mpls
+!
+interface lo
+ ip address 6.6.6.6/32
+!
+interface eth-rt4
+ ip address 10.0.7.6/24
+!
+interface eth-rt5
+ ip address 10.0.8.6/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py b/tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py
new file mode 100644
index 0000000..96e37fd
--- /dev/null
+++ b/tests/topotests/ospf_sr_topo1/test_ospf_sr_topo1.py
@@ -0,0 +1,682 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_sr_topo1.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_ospf_sr_topo1.py:
+
+ +---------+
+ | |
+ | RT1 |
+ | 1.1.1.1 |
+ | |
+ +---------+
+ |eth-sw1
+ |
+ |
+ |
+ +---------+ | +---------+
+ | | | | |
+ | RT2 |eth-sw1 | eth-sw1| RT3 |
+ | 2.2.2.2 +----------+----------+ 3.3.3.3 |
+ | | 10.0.1.0/24 | |
+ +---------+ +---------+
+ eth-rt4-1| |eth-rt4-2 eth-rt5-1| |eth-rt5-2
+ | | | |
+ 10.0.2.0/24| |10.0.3.0/24 10.0.4.0/24| |10.0.5.0/24
+ | | | |
+ eth-rt2-1| |eth-rt2-2 eth-rt3-1| |eth-rt3-2
+ +---------+ +---------+
+ | | | |
+ | RT4 | 10.0.6.0/24 | RT5 |
+ | 4.4.4.4 +---------------------+ 5.5.5.5 |
+ | |eth-rt5 eth-rt4| |
+ +---------+ +---------+
+ eth-rt6| |eth-rt6
+ | |
+ 10.0.7.0/24| |10.0.8.0/24
+ | +---------+ |
+ | | | |
+ | | RT6 | |
+ +----------+ 6.6.6.6 +-----------+
+ eth-rt4| |eth-rt5
+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-sw1")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-sw1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-1")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4-2")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2-2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-1")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-1")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5-2")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3-2")
+
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s7")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt4")
+
+ switch = tgen.add_switch("s8")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt6")
+ switch.add_link(tgen.gears["rt6"], nodeif="eth-rt5")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ 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 print_cmd_result(rname, command):
+ print(get_topogen().gears[rname].vtysh_cmd(command, isjson=False))
+
+
+def router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+#
+# Step 1
+#
+# Test initial network convergence
+#
+def test_rib_step1():
+ logger.info("Test (step 1): verify RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step1/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step1():
+ logger.info("Test (step 1): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step1/show_mpls_table.ref"
+ )
+
+
+#
+# Step 2
+#
+# Action(s):
+# -Disable OSPF on the eth-rt5 interface on rt4
+#
+# Expected changes:
+# -rt4 should uninstall the Adj-SIDs pointing to rt5
+# -rt5 should uninstall the Adj-SIDs pointing to rt4
+# -rt2 should reinstall rt5's Prefix-SIDs (2 nexthops deleted)
+# -rt3 should reinstall rt4's Prefix-SIDs (2 nexthops deleted)
+# -rt4 should reinstall rt3's Prefix-SIDs (1 nexthop deleted)
+# -rt4 should reinstall rt5's Prefix-SIDs (1 nexthop changed)
+# -rt5 should reinstall rt2's Prefix-SIDs (1 nexthop deleted)
+# -rt5 should reinstall rt4's Prefix-SIDs (1 nexthop changed)
+#
+def test_rib_ipv4_step2():
+ logger.info("Test (step 2): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling OSPF on the eth-rt5 interface on rt4")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt5" -c "no ip ospf network point-to-point"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step2/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step2():
+ logger.info("Test (step 2): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step2/show_mpls_table.ref"
+ )
+
+
+#
+# Step 3
+#
+# Action(s):
+# -Shut down the eth-rt4 interface on rt6
+# -Shut down the eth-rt5 interface on rt6
+#
+# Expected changes:
+# -All routers should uninstall rt6's Prefix-SIDs
+# -rt4 and rt5 should uninstall the Adj-SIDs pointing to rt6
+# -rt4 should reconverge rt5's Prefix-SIDs through rt2 using ECMP
+# -rt5 should reconverge rt4's Prefix-SIDs through rt3 using ECMP
+# -rt6 should uninstall all its IS-IS routes, Prefix-SIDs and Adj-SIDs
+#
+def test_rib_ipv4_step3():
+ logger.info("Test (step 3): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Shutting down the eth-rt4 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "shutdown"')
+ logger.info("Shutting down the eth-rt5 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "shutdown"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step3/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step3():
+ logger.info("Test (step 3): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step3/show_mpls_table.ref"
+ )
+
+
+#
+# Step 4
+#
+# Action(s):
+# -Bring up the eth-rt4 interface on rt6
+# -Bring up the eth-rt5 interface on rt6
+# -Change rt6's SRGB
+#
+# Expected changes:
+# -All routers should install rt6's Prefix-SIDs
+# -rt4 and rt5 should install Adj-SIDs for rt6
+# -rt4 should reconverge rt5's Prefix-SIDs through rt6 using the new SRGB
+# -rt5 should reconverge rt4's Prefix-SIDs through rt6 using the new SRGB
+# -rt6 should reinstall all IS-IS routes and Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5
+#
+def test_rib_ipv4_step4():
+ logger.info("Test (step 4): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Bringing up the eth-rt4 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt4" -c "no shutdown"')
+ logger.info("Bringing up the eth-rt5 interface on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "interface eth-rt5" -c "no shutdown"')
+ logger.info("Changing rt6's SRGB")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 18000 25999"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step4/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step4():
+ logger.info("Test (step 4): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step4/show_mpls_table.ref"
+ )
+
+
+#
+# Step 5
+#
+# Action(s):
+# -Disable SR on rt6
+#
+# Expected changes:
+# -All routers should uninstall rt6's Prefix-SIDs
+# -rt4 should uninstall rt5's Prefix-SIDs since the nexthop router hasn't SR enabled anymore
+# -rt5 should uninstall rt4's Prefix-SIDs since the nexthop router hasn't SR enabled anymore
+# -rt6 should uninstall all Prefix-SIDs from the network, and the Adj-SIDs for rt4 and rt5
+#
+def test_rib_ipv4_step5():
+ logger.info("Test (step 5): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Disabling SR on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "router ospf" -c "no segment-routing on"')
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step5/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step5():
+ logger.info("Test (step 5): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step5/show_mpls_table.ref"
+ )
+
+
+#
+# Step 6
+#
+# Action(s):
+# -Enable SR on rt6
+#
+# Expected changes:
+# -All routers should install rt6's Prefix-SIDs
+# -rt4 should install rt5's Prefix-SIDs through rt6
+# -rt5 should install rt4's Prefix-SIDs through rt6
+# -rt6 should install all Prefix-SIDs from the network, and Adj-SIDs for rt4 and rt5
+#
+def test_rib_ipv4_step6():
+ logger.info("Test (step 6): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Enabling SR on rt6")
+ tgen.net["rt6"].cmd('vtysh -c "conf t" -c "router ospf" -c "segment-routing on"')
+
+ # FIXME: This is currently necessary because the CLI is not yet yang based.
+ logger.info("Re-do rt6's SR prefix-sid config")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 6.6.6.6/32 index 60 explicit-null"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step6/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step6():
+ logger.info("Test (step 6): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step6/show_mpls_table.ref"
+ )
+
+
+#
+# Step 7
+#
+# Action(s):
+# -Delete rt1's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should uninstall rt1's Prefix-SIDs
+#
+def test_rib_ipv4_step7():
+ logger.info("Test (step 7): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Deleting rt1's Prefix-SIDs")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "no segment-routing prefix 1.1.1.1/32 index 10"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step7/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step7():
+ logger.info("Test (step 7): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step7/show_mpls_table.ref"
+ )
+
+
+#
+# Step 8
+#
+# Action(s):
+# -Re-add rt1's Prefix-SIDs
+#
+# Expected changes:
+# -All routers should install rt1's Prefix-SIDs
+#
+def test_rib_ipv4_step8():
+ logger.info("Test (step 8): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Re-adding rt1's Prefix-SIDs")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 1.1.1.1/32 index 10"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step8/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step8():
+ logger.info("Test (step 8): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step8/show_mpls_table.ref"
+ )
+
+
+#
+# Step 9
+#
+# Action(s):
+# -Change rt1's Prefix-SIDs to use the no-php option
+# -Change rt6's Prefix-SIDs to stop using the explicit-null option
+#
+# Expected changes:
+# -rt2 and rt3 should reinstall rt1's Prefix-SIDs accordingly
+# -rt4 and rt5 should reinstall rt6's Prefix-SIDs accordingly
+#
+def test_rib_ipv4_step9():
+ logger.info("Test (step 9): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Changing rt1's Prefix-SIDs to use the no-php option")
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 1.1.1.1/32 index 10 no-php-flag"'
+ )
+
+ logger.info("Change rt6's Prefix-SIDs to stop using the explicit-null option")
+ tgen.net["rt6"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 6.6.6.6/32 index 60"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step9/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step9():
+ logger.info("Test (step 9): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step9/show_mpls_table.ref"
+ )
+
+
+#
+# Step 10
+#
+# Action(s):
+# -Remove the IPv4 address from rt4's eth-rt2-1 interface
+#
+# Expected changes:
+# -rt2 should uninstall the IPv4 Adj-SIDs attached to the eth-rt4-1 interface
+# -rt2 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt4 (ECMP shouldn't be used anymore)
+# -rt4 should reinstall all IPv4 Prefix-SIDs whose nexthop router is rt2 (ECMP shouldn't be used anymore)
+#
+def test_rib_ipv4_step10():
+ logger.info("Test (step 10): verify IPv4 RIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Removing the IPv4 address from rt4's eth-rt2-1 interface")
+ tgen.net["rt4"].cmd(
+ 'vtysh -c "conf t" -c "interface eth-rt2-1" -c "no ip address 10.0.2.4/24"'
+ )
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show ip route ospf json", "step10/show_ip_route.ref"
+ )
+
+
+def test_mpls_lib_step10():
+ logger.info("Test (step 10): verify MPLS LIB")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ for rname in ["rt1", "rt2", "rt3", "rt4", "rt5", "rt6"]:
+ router_compare_json_output(
+ rname, "show mpls table json", "step10/show_mpls_table.ref"
+ )
+
+
+# FIXME: These tests don't work yet, this should be fixed with the
+# switchover to a yang based CLI.
+#
+# Step 11
+#
+# Action(s):
+# -Enter invalid SR configuration
+#
+# Expected changes:
+# -All commands should be rejected
+#
+# def test_ospf_invalid_config_step11():
+# logger.info("Test (step 11): check if invalid configuration is rejected")
+# tgen = get_topogen()
+#
+# # Skip if previous fatal error condition is raised
+# if tgen.routers_have_failure():
+# pytest.skip(tgen.errors)
+#
+# logger.info("Entering invalid Segment Routing configuration...")
+# ret = tgen.net["rt1"].cmd(
+# 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 1.1.1.1/32 index 10000"'
+# )
+# assert (
+# re.search("Configuration failed", ret) is not None
+# ), "Invalid SR configuration wasn't rejected"
+# ret = tgen.net["rt1"].cmd(
+# 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 16000 14999"'
+# )
+# assert (
+# re.search("Configuration failed", ret) is not None
+# ), "Invalid SR configuration wasn't rejected"
+# ret = tgen.net["rt1"].cmd(
+# 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 16000 16001"'
+# )
+# assert (
+# re.search("Configuration failed", ret) is not None
+# ), "Invalid SR configuration wasn't rejected"
+
+
+# Memory leak test template
+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/ospf_suppress_fa/__init__.py b/tests/topotests/ospf_suppress_fa/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/__init__.py
diff --git a/tests/topotests/ospf_suppress_fa/r1/ospfd.conf b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf
new file mode 100644
index 0000000..c02be35
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r1/ospfd.conf
@@ -0,0 +1,9 @@
+!
+interface r1-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 10.0.12.0/24 area 0
+!
diff --git a/tests/topotests/ospf_suppress_fa/r1/zebra.conf b/tests/topotests/ospf_suppress_fa/r1/zebra.conf
new file mode 100644
index 0000000..c1e31fb
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+interface r1-eth0
+ ip address 10.0.12.1/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/r2/ospfd.conf b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf
new file mode 100644
index 0000000..ebc7d25
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r2/ospfd.conf
@@ -0,0 +1,16 @@
+!
+interface r2-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ network 10.0.12.0/24 area 0
+ network 10.0.23.0/24 area 1
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_suppress_fa/r2/zebra.conf b/tests/topotests/ospf_suppress_fa/r2/zebra.conf
new file mode 100644
index 0000000..9f1a263
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.12.2/24
+!
+interface r2-eth1
+ ip address 10.0.23.2/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/r3/ospfd.conf b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf
new file mode 100644
index 0000000..08be11a
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r3/ospfd.conf
@@ -0,0 +1,11 @@
+!
+interface r3-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ redistribute static
+ network 10.0.23.0/24 area 1
+ area 1 nssa
+!
diff --git a/tests/topotests/ospf_suppress_fa/r3/zebra.conf b/tests/topotests/ospf_suppress_fa/r3/zebra.conf
new file mode 100644
index 0000000..f76cbf7
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/r3/zebra.conf
@@ -0,0 +1,8 @@
+!
+ip route 3.3.1.1/32 Null0
+ip route 3.3.2.2/32 Null0
+ip route 3.3.3.3/32 Null0
+!
+interface r3-eth0
+ ip address 10.0.23.3/24
+!
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot
new file mode 100644
index 0000000..1036658
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot
@@ -0,0 +1,66 @@
+## 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 ospf_topo1 {
+ label="ospf suppress-fa";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.12.1",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ label="r2 (ABR)\nrtr-id 10.0.23.2",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ label="r3 (ASBR)\nrtr-id 10.0.23.3",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ label="s1\n10.0.12.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.23.0/24",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="area 0"
+ r1 -- s1 [label="eth1\n.1"];
+ r2 -- s1 [label="eth1\n.2"];
+ }
+
+ subgraph cluster1 {
+ label="area 1\nNSSA"
+ r2 -- s2 [label="eth2\n.2"];
+ r3 -- s2 [label="eth1\n.3"];
+ }
+
+ { rank=same; r1; r2; r3; }
+}
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg
new file mode 100644
index 0000000..2907d79
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.dot.jpg
Binary files differ
diff --git a/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
new file mode 100644
index 0000000..5903649
--- /dev/null
+++ b/tests/topotests/ospf_suppress_fa/test_ospf_suppress_fa.py
@@ -0,0 +1,188 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_suppres_fa.py
+# Carles Kishimoto
+#
+# 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_ospf_suppres_fa.py: Test OSPF suppress-fa feature
+- Topology: r1 --- R2 (ABR) --- R3 (redistribute static)
+
+test_ospf_set_suppress_fa()
+ 1) R1: Get a dict[LSA_ID] = fwd_addr for all type 5 LSA
+ 2) R2: Configure: area 1 nssa suppress-fa
+ 3) R1: Get a dict[LSA_ID] and compare fwd_address with 0.0.0.0
+
+test_ospf_unset_suppress_fa()
+ 4) R2: Configure: no area 1 nssa suppress-fa
+ 5) R1: Get a dict[LSA_ID] = fwd_addr and compare it with the dict obtained in 1)
+"""
+
+import os
+import sys
+import re
+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
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create routers
+ for router in range(1, 4):
+ tgen.add_router("r{}".format(router))
+
+ # R1-R2 backbone area
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # R2-R3 NSSA area
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra and ospf configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+
+def ospf_configure_suppress_fa(router_name, area):
+ "Configure OSPF suppress-fa in router_name"
+
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ router.vtysh_cmd(
+ "conf t\nrouter ospf\narea {} nssa suppress-fa\nexit\n".format(area)
+ )
+
+
+def ospf_unconfigure_suppress_fa(router_name, area):
+ "Remove OSPF suppress-fa in router_name"
+
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ router.vtysh_cmd(
+ "conf t\nrouter ospf\nno area {} nssa suppress-fa\nexit\n".format(area)
+ )
+
+
+def ospf_get_lsa_type5(router_name):
+ "Return a dict with link state id as key and forwarding addresses as value"
+
+ result = dict()
+ tgen = get_topogen()
+ router = tgen.gears[router_name]
+ cmd = "show ip ospf database external\n"
+ output = topotest.normalize_text(router.vtysh_cmd(cmd))
+ for line in output.splitlines():
+ re0 = re.match(r"\s+Link State ID: (\S+) \(External Network Number\)", line)
+ if re0:
+ lsa = re0.group(1)
+ re1 = re.match(r"\s+Forward Address: (\S+)", line)
+ if re1:
+ result[lsa] = re1.group(1)
+ return result
+
+
+@pytest.fixture(scope="module", name="original")
+def test_ospf_set_suppress_fa():
+ "Test OSPF area [x] nssa suppress-fa"
+
+ # Get current forwarding address for each LSA type-5 in r1
+ initial = ospf_get_lsa_type5("r1")
+
+ # Configure suppres-fa in r2 area 1
+ ospf_configure_suppress_fa("r2", "1")
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+ # Check forwarding address on r1 for all statics is 0.0.0.0
+ assertmsg = "Forwarding address is not 0.0.0.0 after enabling OSPF suppress-fa"
+ suppress = ospf_get_lsa_type5("r1")
+ for prefix in suppress:
+ assert suppress[prefix] == "0.0.0.0", assertmsg
+
+ # Return the original forwarding addresses so we can compare them
+ # in the test_ospf_unset_supress_fa
+ return initial
+
+
+def test_ospf_unset_supress_fa(original):
+ "Test OSPF no area [x] nssa suppress-fa"
+
+ # Remove suppress-fa in r2 area 1
+ ospf_unconfigure_suppress_fa("r2", "1")
+ topotest.sleep(10, "Waiting for OSPF convergence")
+
+ # Check forwarding address is the original value on r1 for all statics
+ assertmsg = "Forwarding address is not correct after removing OSPF suppress-fa"
+ restore = ospf_get_lsa_type5("r1")
+ for prefix in restore:
+ assert restore[prefix] == original[prefix], assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospf_te_topo1/__init__.py b/tests/topotests/ospf_te_topo1/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/__init__.py
diff --git a/tests/topotests/ospf_te_topo1/r1/ospfd.conf b/tests/topotests/ospf_te_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..312dd26
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r1/ospfd.conf
@@ -0,0 +1,23 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r1-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r1-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.255.1
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.1
+!
+
diff --git a/tests/topotests/ospf_te_topo1/r1/zebra.conf b/tests/topotests/ospf_te_topo1/r1/zebra.conf
new file mode 100644
index 0000000..7c5dc3f
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r1/zebra.conf
@@ -0,0 +1,21 @@
+!
+interface lo
+ ip address 10.0.255.1/32
+!
+interface r1-eth0
+ ip address 10.0.0.1/24
+ link-params
+ metric 20
+ delay 10000
+ ava-bw 1.25e+08
+ enable
+ exit-link-params
+!
+interface r1-eth1
+ ip address 10.0.1.1/24
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_te_topo1/r2/ospfd.conf b/tests/topotests/ospf_te_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..e9c3f65
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r2/ospfd.conf
@@ -0,0 +1,34 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r2-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+interface r2-eth2
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r2-eth3
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+router ospf
+ ospf router-id 10.0.255.2
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.2
+!
diff --git a/tests/topotests/ospf_te_topo1/r2/zebra.conf b/tests/topotests/ospf_te_topo1/r2/zebra.conf
new file mode 100644
index 0000000..69e1019
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r2/zebra.conf
@@ -0,0 +1,33 @@
+!
+interface lo
+ ip address 10.0.255.2/32
+!
+interface r2-eth0
+ ip address 10.0.0.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth1
+ ip address 10.0.1.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth2
+ ip address 10.0.3.2/24
+ link-params
+ enable
+ exit-link-params
+!
+interface r2-eth3
+ ip address 10.0.4.2/24
+ link-params
+ metric 30
+ delay 25000
+ use-bw 1.25e+8
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_te_topo1/r3/ospfd.conf b/tests/topotests/ospf_te_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..caa5f1e
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r3/ospfd.conf
@@ -0,0 +1,24 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r3-eth0
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+interface r3-eth1
+ ip ospf network point-to-point
+ ip ospf area 0.0.0.0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+!
+router ospf
+ ospf router-id 10.0.255.3
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.3
+ mpls-te inter-as as
+!
diff --git a/tests/topotests/ospf_te_topo1/r3/zebra.conf b/tests/topotests/ospf_te_topo1/r3/zebra.conf
new file mode 100644
index 0000000..4cf9077
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+!
+interface lo
+ ip address 10.0.255.3/32
+!
+interface r3-eth0
+ ip address 10.0.3.1/24
+ link-params
+ enable
+ admin-grp 0x20
+ exit-link-params
+!
+interface r3-eth1
+ ip address 10.0.5.1/24
+ link-params
+ enable
+ metric 10
+ delay 50000
+ neighbor 10.0.255.5 as 65535
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_te_topo1/r4/ospfd.conf b/tests/topotests/ospf_te_topo1/r4/ospfd.conf
new file mode 100644
index 0000000..cd50801
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r4/ospfd.conf
@@ -0,0 +1,21 @@
+!
+interface lo
+ ip ospf area 0.0.0.0
+!
+interface r4-eth0
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 0.0.0.0
+!
+!
+router ospf
+ ospf router-id 10.0.255.4
+ capability opaque
+ mpls-te on
+ mpls-te router-address 10.0.255.4
+ segment-routing on
+ segment-routing global-block 10000 19999 local-block 5000 5999
+ segment-routing node-msd 12
+ segment-routing prefix 10.0.255.4/32 index 400 no-php-flag
+!
diff --git a/tests/topotests/ospf_te_topo1/r4/zebra.conf b/tests/topotests/ospf_te_topo1/r4/zebra.conf
new file mode 100644
index 0000000..18c003b
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/r4/zebra.conf
@@ -0,0 +1,12 @@
+!
+interface lo
+ ip address 10.0.255.4/32
+!
+interface r4-eth0
+ ip address 10.0.4.1/24
+ link-params
+ enable
+ exit-link-params
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step1.json b/tests/topotests/ospf_te_topo1/reference/ted_step1.json
new file mode 100644
index 0000000..d6bfca6
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step1.json
@@ -0,0 +1,571 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":5,
+ "edgesCount":9,
+ "subnetsCount":14,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ },
+ {
+ "vertex-id":167837445,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.5",
+ "vertex-type":"Remote ASBR",
+ "asn":65535
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837445,
+ "metric":0,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.5.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "remote-asn":65535,
+ "remote-as-address":"10.0.255.5",
+ "delay":50000
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.5.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.5\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.5",
+ "vertex-id":167837445,
+ "metric":10
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step2.json b/tests/topotests/ospf_te_topo1/reference/ted_step2.json
new file mode 100644
index 0000000..ec30af6
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step2.json
@@ -0,0 +1,473 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":5,
+ "edgesCount":7,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ },
+ {
+ "vertex-id":167837445,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.5",
+ "vertex-type":"Remote ASBR",
+ "asn":65535
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167773441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837445,
+ "metric":0,
+ "edge-attributes":{
+ "te-metric":10,
+ "local-address":"10.0.5.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "remote-asn":65535,
+ "remote-as-address":"10.0.255.5",
+ "delay":50000
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.5.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.5\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.5",
+ "vertex-id":167837445,
+ "metric":10
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step3.json b/tests/topotests/ospf_te_topo1/reference/ted_step3.json
new file mode 100644
index 0000000..853704b
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step3.json
@@ -0,0 +1,405 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":6,
+ "subnetsCount":10,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard"
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ }
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ }
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step4.json b/tests/topotests/ospf_te_topo1/reference/ted_step4.json
new file mode 100644
index 0000000..0aa5771
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step4.json
@@ -0,0 +1,486 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":6,
+ "subnetsCount":10,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step5.json b/tests/topotests/ospf_te_topo1/reference/ted_step5.json
new file mode 100644
index 0000000..07637f3
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step5.json
@@ -0,0 +1,608 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":8,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000,
+ "utilized-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step6.json b/tests/topotests/ospf_te_topo1/reference/ted_step6.json
new file mode 100644
index 0000000..e9eee96
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step6.json
@@ -0,0 +1,609 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":4,
+ "edgesCount":8,
+ "subnetsCount":12,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ },
+ {
+ "vertex-id":167837444,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.4",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":10000,
+ "srgb-lower":10000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":5000,
+ "msd":12
+ }
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773185,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "local-vertex-id":167837444,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.4.1",
+ "remote-address":"10.0.4.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":20000,
+ "jitter":10000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167773186,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837444,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":30,
+ "local-address":"10.0.4.2",
+ "remote-address":"10.0.4.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":25000
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5005,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5004,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.4.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ },
+ {
+ "subnet-id":"10.0.255.4\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.4",
+ "vertex-id":167837444,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":400,
+ "algo":0,
+ "flags":"0x40"
+ }
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/reference/ted_step7.json b/tests/topotests/ospf_te_topo1/reference/ted_step7.json
new file mode 100644
index 0000000..f912ae4
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/reference/ted_step7.json
@@ -0,0 +1,451 @@
+{
+ "ted":{
+ "name":"OSPF",
+ "key":1,
+ "verticesCount":3,
+ "edgesCount":6,
+ "subnetsCount":9,
+ "vertices":[
+ {
+ "vertex-id":167837441,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.1",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":4000,
+ "srgb-lower":20000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":1000,
+ "srlb-lower":15000
+ }
+ },
+ {
+ "vertex-id":167837442,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.2",
+ "vertex-type":"Standard",
+ "segment-routing":{
+ "srgb-size":8000,
+ "srgb-lower":16000,
+ "algorithms":[
+ {
+ "0":"SPF"
+ }
+ ],
+ "srlb-size":2000,
+ "srlb-lower":5000,
+ "msd":16
+ }
+ },
+ {
+ "vertex-id":167837443,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "router-id":"10.0.255.3",
+ "vertex-type":"ASBR"
+ }
+ ],
+ "edges":[
+ {
+ "edge-id":167772161,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "te-metric":20,
+ "local-address":"10.0.0.1",
+ "remote-address":"10.0.0.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ],
+ "delay":10000,
+ "available-bandwidth":125000000.0
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772162,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.0.2",
+ "remote-address":"10.0.0.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5001,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5000,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772417,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "local-vertex-id":167837441,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.1",
+ "remote-address":"10.0.1.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":15003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":15002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772418,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837441,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.1.2",
+ "remote-address":"10.0.1.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5007,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5006,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ },
+ {
+ "edge-id":167772929,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "local-vertex-id":167837443,
+ "remote-vertex-id":167837442,
+ "metric":10,
+ "edge-attributes":{
+ "admin-group":32,
+ "local-address":"10.0.3.1",
+ "remote-address":"10.0.3.2",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ }
+ },
+ {
+ "edge-id":167772930,
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "local-vertex-id":167837442,
+ "remote-vertex-id":167837443,
+ "metric":10,
+ "edge-attributes":{
+ "local-address":"10.0.3.2",
+ "remote-address":"10.0.3.1",
+ "max-link-bandwidth":176258176.0,
+ "max-resv-link-bandwidth":176258176.0,
+ "unreserved-bandwidth":[
+ {
+ "class-type-0":176258176.0
+ },
+ {
+ "class-type-1":176258176.0
+ },
+ {
+ "class-type-2":176258176.0
+ },
+ {
+ "class-type-3":176258176.0
+ },
+ {
+ "class-type-4":176258176.0
+ },
+ {
+ "class-type-5":176258176.0
+ },
+ {
+ "class-type-6":176258176.0
+ },
+ {
+ "class-type-7":176258176.0
+ }
+ ]
+ },
+ "segment-routing":[
+ {
+ "adj-sid":5003,
+ "flags":"0x60",
+ "weight":0
+ },
+ {
+ "adj-sid":5002,
+ "flags":"0xe0",
+ "weight":0
+ }
+ ]
+ }
+ ],
+ "subnets":[
+ {
+ "subnet-id":"10.0.0.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.0.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.1.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.3.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":10
+ },
+ {
+ "subnet-id":"10.0.255.1\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.1",
+ "vertex-id":167837441,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":10,
+ "algo":0,
+ "flags":"0x0"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.2\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.2",
+ "vertex-id":167837442,
+ "metric":0,
+ "segment-routing":{
+ "pref-sid":20,
+ "algo":0,
+ "flags":"0x50"
+ }
+ },
+ {
+ "subnet-id":"10.0.255.3\/32",
+ "status":"Sync",
+ "origin":"OSPFv2",
+ "advertised-router":"10.0.255.3",
+ "vertex-id":167837443,
+ "metric":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
new file mode 100644
index 0000000..699cdc9
--- /dev/null
+++ b/tests/topotests/ospf_te_topo1/test_ospf_te_topo1.py
@@ -0,0 +1,295 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_te_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 by Orange
+# Author: Olivier Dugeon <olivier.dugeon@orange.com>
+#
+# 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_ospf_te_topo1.py: Test the FRR OSPF with Traffic Engineering.
+
+ +------------+
+ | |
+ | R1 |
+ | 10.0.225.1 |
+ | |
+ +------------+
+ r1-eth0| |r1-eth1
+ | |
+ 10.0.0.0/24| |10.0.1.0/24
+ | |
+ r2-eth0| |r2-eth1
+ +------------+ +------------+
+ | | | |
+ | R2 |r2-eth2 r3-eth0| R3 |
+ | 10.0.255.2 +------------------+ 10.0.255.3 |
+ | | 10.0.3.0/24 | |
+ +------------+ +------+-----+
+ r2-eth3| r3-eth1|
+ | |
+ 10.0.4.0/24| 10.0.5.0/24|
+ | |
+ r4-eth0| V
+ +------------+ ASBR 10.0.255.5
+ | |
+ | R4 |
+ | 10.0.255.4 |
+ | |
+ +------------+
+
+"""
+
+import os
+import sys
+import json
+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
+# Required to instantiate the topology builder class.
+
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# and Finally pytest
+import pytest
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Interconect router 1 and 2 with 2 links
+ 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["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 3 and 2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 4 and 2
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r4"])
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconnect router 3 with next AS
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ logger.info("\n\n---- Starting OSPF TE tests ----\n")
+
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module():
+ "Teardown the pytest environment"
+
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+ logger.info("\n\n---- OSPF TE tests End ----\n")
+
+
+def compare_ted_json_output(tgen, rname, fileref):
+ "Compare TED JSON output"
+
+ logger.info('Comparing router "%s" TED output', rname)
+
+ filename = "{}/reference/{}".format(CWD, fileref)
+ expected = json.loads(open(filename).read())
+ command = "show ip ospf mpls-te database json"
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = '"{}" TED JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def setup_testcase(msg):
+ "Setup test case"
+
+ logger.info(msg)
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ return tgen
+
+
+# Note that all routers must discover the same Network Topology, so the same TED.
+
+
+def test_step1():
+ "Step1: Check initial topology"
+
+ tgen = setup_testcase("Step1: test initial OSPF TE Data Base")
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step1.json")
+
+
+def test_step2():
+ "Step2: Shutdown interface between r1 and r2 and verify that \
+ corresponding Edges are removed from the TED on all routers "
+
+ tgen = setup_testcase("Step2: Shutdown interface between r1 & r2")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "shutdown"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "shutdown"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step2.json")
+
+
+def test_step3():
+ "Step3: Disable Inter-AS on r3 and verify that corresponding Edge and \
+ remote ASBR are removed from the TED on all routers"
+
+ tgen = setup_testcase("Step3: Disable Inter-AS on r3")
+
+ tgen.net["r3"].cmd('vtysh -c "conf t" -c "router ospf" -c "no mpls-te inter-as"')
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step3.json")
+
+
+def test_step4():
+ "Step4: Enable Segment Routing on r1 and r2 and verify that corresponding \
+ Edges are updated with Adjacency SID and Subnets with Prefix SID in the \
+ TED on all routers"
+
+ tgen = setup_testcase("Step4: Enable Segment Routing on r1 & r2")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "router ospf" -c "segment-routing on"')
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 20000 23999"'
+ )
+ tgen.net["r1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.1/32 index 10"'
+ )
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "router ospf" -c "segment-routing on"')
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing node-msd 16"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing global-block 16000 23999 local-block 5000 6999"'
+ )
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "segment-routing prefix 10.0.255.2/32 index 20 explicit-null"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step4.json")
+
+
+def test_step5():
+ "Step5: Re-enable interface between r1 & r2 and verify that corresponding \
+ Edges are added in the TED on all routers"
+
+ tgen = setup_testcase("Step5: Re-enable interface between r1 & r2")
+
+ tgen.net["r1"].cmd('vtysh -c "conf t" -c "interface r1-eth1" -c "no shutdown"')
+ tgen.net["r2"].cmd('vtysh -c "conf t" -c "interface r2-eth1" -c "no shutdown"')
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step5.json")
+
+
+def test_step6():
+ "Step6: Set delay and jitter for interface r4-eth0 on r4, remove use-bw \
+ for interface r2-eth3 on r2 and verify that corresponding Edges are \
+ updated in the TED on all routers"
+
+ tgen = setup_testcase("Step6: Modify link parameters on r2 & r4")
+
+ tgen.net["r2"].cmd(
+ 'vtysh -c "conf t" -c "interface r2-eth3" -c "link-params" -c "no use-bw"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay 20000"'
+ )
+ tgen.net["r4"].cmd(
+ 'vtysh -c "conf t" -c "interface r4-eth0" -c "link-params" -c "delay-variation 10000"'
+ )
+
+ for rname in ["r1", "r2", "r3", "r4"]:
+ compare_ted_json_output(tgen, rname, "ted_step6.json")
+
+
+def test_step7():
+ "Step7: Disable OSPF on r4 and verify that corresponding Vertex, Edges and \
+ Subnets are removed from the TED on all remaining routers"
+
+ tgen = setup_testcase("Step7: Disable OSPF on r4")
+
+ tgen.net["r4"].cmd('vtysh -c "conf t" -c "no router ospf"')
+
+ for rname in ["r1", "r2", "r3"]:
+ compare_ted_json_output(tgen, rname, "ted_step7.json")
+
+
+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/ospf_tilfa_topo1/__init__.py b/tests/topotests/ospf_tilfa_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/__init__.py
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf b/tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf
new file mode 100644
index 0000000..04b2c38
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/ospfd.conf
@@ -0,0 +1,27 @@
+! debug ospf sr
+! debug ospf ti-lfa
+!
+interface lo
+!
+interface eth-rt2
+ ip ospf network point-to-point
+!
+interface eth-rt3
+ ip ospf network point-to-point
+!
+router ospf
+ ospf router-id 1.1.1.1
+ network 1.1.1.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 1.1.1.0/24
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.1
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.1/32 index 10
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/step1/show_ip_route_initial.ref b/tests/topotests/ospf_tilfa_topo1/rt1/step1/show_ip_route_initial.ref
new file mode 100644
index 0000000..0ad2aae
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/step1/show_ip_route_initial.ref
@@ -0,0 +1,156 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "1.1.1.2\/32":[
+ {
+ "prefix":"1.1.1.2\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.3\/32":[
+ {
+ "prefix":"1.1.1.3\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "1.1.1.4\/32":[
+ {
+ "prefix":"1.1.1.4\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.5\/32":[
+ {
+ "prefix":"1.1.1.5\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ },
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_initial.ref b/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_initial.ref
new file mode 100644
index 0000000..0ad2aae
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_initial.ref
@@ -0,0 +1,156 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "1.1.1.2\/32":[
+ {
+ "prefix":"1.1.1.2\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.3\/32":[
+ {
+ "prefix":"1.1.1.3\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "1.1.1.4\/32":[
+ {
+ "prefix":"1.1.1.4\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.5\/32":[
+ {
+ "prefix":"1.1.1.5\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ },
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_link_protection.ref b/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_link_protection.ref
new file mode 100644
index 0000000..968570e
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/step2/show_ip_route_link_protection.ref
@@ -0,0 +1,226 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "1.1.1.2\/32":[
+ {
+ "prefix":"1.1.1.2\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "1.1.1.3\/32":[
+ {
+ "prefix":"1.1.1.3\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "1.1.1.4\/32":[
+ {
+ "prefix":"1.1.1.4\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2",
+ "labels":[
+ 16040
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050,
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "1.1.1.5\/32":[
+ {
+ "prefix":"1.1.1.5\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3",
+ "labels":[
+ 16050
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040,
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ },
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040
+ ]
+ },
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_initial.ref b/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_initial.ref
new file mode 100644
index 0000000..0ad2aae
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_initial.ref
@@ -0,0 +1,156 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "1.1.1.2\/32":[
+ {
+ "prefix":"1.1.1.2\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.3\/32":[
+ {
+ "prefix":"1.1.1.3\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "1.1.1.4\/32":[
+ {
+ "prefix":"1.1.1.4\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.5\/32":[
+ {
+ "prefix":"1.1.1.5\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ },
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_node_protection.ref b/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_node_protection.ref
new file mode 100644
index 0000000..46a80d2
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/step3/show_ip_route_node_protection.ref
@@ -0,0 +1,192 @@
+{
+ "1.1.1.1\/32":[
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ },
+ {
+ "prefix":"1.1.1.1\/32",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"lo"
+ }
+ ]
+ }
+ ],
+ "1.1.1.2\/32":[
+ {
+ "prefix":"1.1.1.2\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "1.1.1.3\/32":[
+ {
+ "prefix":"1.1.1.3\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "1.1.1.4\/32":[
+ {
+ "prefix":"1.1.1.4\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2",
+ "labels":[
+ 16040
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050
+ ]
+ }
+ ]
+ }
+ ],
+ "1.1.1.5\/32":[
+ {
+ "prefix":"1.1.1.5\/32",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3",
+ "labels":[
+ 16050
+ ]
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ],
+ "10.0.1.0\/24":[
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.2.0\/24":[
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.2.0\/24",
+ "protocol":"connected",
+ "nexthops":[
+ {
+ "directlyConnected":true,
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.3.0\/24":[
+ {
+ "prefix":"10.0.3.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ }
+ ]
+ }
+ ],
+ "10.0.4.0\/24":[
+ {
+ "prefix":"10.0.4.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ]
+ }
+ ],
+ "10.0.5.0\/24":[
+ {
+ "prefix":"10.0.5.0\/24",
+ "protocol":"ospf",
+ "nexthops":[
+ {
+ "ip":"10.0.1.2",
+ "interfaceName":"eth-rt2"
+ },
+ {
+ "ip":"10.0.2.2",
+ "interfaceName":"eth-rt3"
+ }
+ ],
+ "backupNexthops":[
+ {
+ "ip":"10.0.2.2",
+ "labels":[
+ 16050
+ ]
+ },
+ {
+ "ip":"10.0.1.2",
+ "labels":[
+ 16040
+ ]
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf b/tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf
new file mode 100644
index 0000000..bf0e77a
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt1/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname rt1
+!
+interface lo
+ ip address 1.1.1.1/32
+!
+interface eth-rt2
+ ip address 10.0.1.1/24
+!
+interface eth-rt3
+ ip address 10.0.2.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf b/tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf
new file mode 100644
index 0000000..e6e4847
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt2/ospfd.conf
@@ -0,0 +1,27 @@
+! debug ospf sr
+! debug ospf ti-lfa
+!
+interface lo
+!
+interface eth-rt1
+ ip ospf network point-to-point
+!
+interface eth-rt4
+ ip ospf network point-to-point
+!
+router ospf
+ ospf router-id 1.1.1.2
+ network 1.1.1.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 1.1.1.0/24
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.2
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.2/32 index 20
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf b/tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf
new file mode 100644
index 0000000..add2933
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt2/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname rt2
+!
+interface lo
+ ip address 1.1.1.2/32
+!
+interface eth-rt1
+ ip address 10.0.1.2/24
+!
+interface eth-rt4
+ ip address 10.0.3.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf b/tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf
new file mode 100644
index 0000000..472cdc6
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt3/ospfd.conf
@@ -0,0 +1,27 @@
+! debug ospf sr
+! debug ospf ti-lfa
+!
+interface lo
+!
+interface eth-rt1
+ ip ospf network point-to-point
+!
+interface eth-rt5
+ ip ospf network point-to-point
+!
+router ospf
+ ospf router-id 1.1.1.3
+ network 1.1.1.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 1.1.1.0/24
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.3
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.3/32 index 30
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf b/tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf
new file mode 100644
index 0000000..1bb64bc
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt3/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname rt3
+!
+interface lo
+ ip address 1.1.1.3/32
+!
+interface eth-rt1
+ ip address 10.0.2.2/24
+!
+interface eth-rt5
+ ip address 10.0.4.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf b/tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf
new file mode 100644
index 0000000..75770dc
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt4/ospfd.conf
@@ -0,0 +1,27 @@
+! debug ospf sr
+! debug ospf ti-lfa
+!
+interface lo
+!
+interface eth-rt2
+ ip ospf network point-to-point
+!
+interface eth-rt5
+ ip ospf network point-to-point
+!
+router ospf
+ ospf router-id 1.1.1.4
+ network 1.1.1.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 1.1.1.0/24
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.4
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.4/32 index 40
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf b/tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf
new file mode 100644
index 0000000..306f0d4
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt4/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname rt4
+!
+interface lo
+ ip address 1.1.1.4/32
+!
+interface eth-rt2
+ ip address 10.0.3.2/24
+!
+interface eth-rt5
+ ip address 10.0.5.1/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf b/tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf
new file mode 100644
index 0000000..ef9d583
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt5/ospfd.conf
@@ -0,0 +1,27 @@
+! debug ospf sr
+! debug ospf ti-lfa
+!
+interface lo
+!
+interface eth-rt3
+ ip ospf network point-to-point
+!
+interface eth-rt4
+ ip ospf network point-to-point
+!
+router ospf
+ ospf router-id 1.1.1.5
+ network 1.1.1.0/24 area 0.0.0.0
+ network 10.0.0.0/16 area 0.0.0.0
+ area 0.0.0.0 range 10.0.0.0/16
+ area 0.0.0.0 range 1.1.1.0/24
+ capability opaque
+ mpls-te on
+ mpls-te router-address 1.1.1.5
+ router-info area 0.0.0.0
+ passive-interface lo
+ segment-routing on
+ segment-routing global-block 16000 23999
+ segment-routing node-msd 8
+ segment-routing prefix 1.1.1.5/32 index 50
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf b/tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf
new file mode 100644
index 0000000..46f7595
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/rt5/zebra.conf
@@ -0,0 +1,17 @@
+log file zebra.log
+!
+hostname rt5
+!
+interface lo
+ ip address 1.1.1.5/32
+!
+interface eth-rt3
+ ip address 10.0.4.2/24
+!
+interface eth-rt4
+ ip address 10.0.5.2/24
+!
+ip forwarding
+!
+line vty
+!
diff --git a/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py b/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
new file mode 100644
index 0000000..696cb90
--- /dev/null
+++ b/tests/topotests/ospf_tilfa_topo1/test_ospf_tilfa_topo1.py
@@ -0,0 +1,239 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_tilfa_topo1.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_ospf_tilfa_topo1.py:
+
+This topology is intentionally kept simple, its main purpose is to verify that
+generated backup label stacks are inserted correctly into the RIB. For fancy
+topologies please use the unit test framework provided in `/tests/ospfd`.
+
+
+ +---------+ +---------+
+ | | | |
+ 10.0.1.0/24 eth+rt1| RT2 |eth+rt4 eth+rt2| RT2 |
+ +---------------------+ 2.2.2.2 +---------------------+ 4.4.4.4 |
+ | | | 10.0.3.0/24 | |
+ |eth+rt2 +---------+ +---------+
+ +---------+ eth+rt5|
+ | | |
+ | RT1 | 10.0.5.0/24|
+ | 1.1.1.1 | |
+ | | |
+ +---------+ eth+rt4|
+ |eth+rt3 +---------+ +---------+
+ | | | 10.0.4.0/24 | |
+ +---------------------+ RT3 +---------------------+ RT5 |
+ 10.0.2.0/24 eth+rt1| 3.3.3.3 |eth+rt5 eth-rt3| 5.5.5.5 |
+ | | | |
+ +---------+ +---------+
+"""
+
+import os
+import sys
+import pytest
+import json
+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.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ #
+ # Define FRR Routers
+ #
+ for router in ["rt1", "rt2", "rt3", "rt4", "rt5"]:
+ tgen.add_router(router)
+
+ #
+ # Define connections
+ #
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt2")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["rt1"], nodeif="eth-rt3")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt1")
+
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["rt2"], nodeif="eth-rt4")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt2")
+
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["rt3"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt3")
+
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["rt4"], nodeif="eth-rt5")
+ switch.add_link(tgen.gears["rt5"], nodeif="eth-rt4")
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ 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 router_compare_json_output(rname, command, reference):
+ "Compare router JSON output"
+
+ logger.info('Comparing router "%s" "%s" output', rname, command)
+
+ tgen = get_topogen()
+ filename = "{}/{}/{}".format(CWD, rname, reference)
+ expected = json.loads(open(filename).read())
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(topotest.router_json_cmp, tgen.gears[rname], command, expected)
+ _, diff = topotest.run_and_expect(test_func, None, count=120, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches the expected result'.format(rname)
+ assert diff is None, assertmsg
+
+
+def test_ospf_initial_convergence_step1():
+ logger.info("Test (step 1): check initial convergence")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router_compare_json_output(
+ "rt1",
+ "show ip route json",
+ "step1/show_ip_route_initial.ref",
+ )
+
+
+def test_ospf_link_protection_step2():
+ logger.info("Test (step 2): check OSPF link protection")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # enable TI-LFA link protection on all interfaces
+ tgen.net["rt1"].cmd('vtysh -c "conf t" -c "router ospf" -c "fast-reroute ti-lfa"')
+
+ router_compare_json_output(
+ "rt1",
+ "show ip route json",
+ "step2/show_ip_route_link_protection.ref",
+ )
+
+ # disable TI-LFA link protection on all interfaces
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "no fast-reroute ti-lfa"'
+ )
+
+ # check if we got back to the initial route table
+ router_compare_json_output(
+ "rt1",
+ "show ip route json",
+ "step2/show_ip_route_initial.ref",
+ )
+
+
+def test_ospf_node_protection_step3():
+ logger.info("Test (step 3): check OSPF node protection")
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # enable TI-LFA node protection on all interfaces
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "fast-reroute ti-lfa node-protection"'
+ )
+
+ router_compare_json_output(
+ "rt1",
+ "show ip route json",
+ "step3/show_ip_route_node_protection.ref",
+ )
+
+ # disable TI-LFA node protection on all interfaces
+ tgen.net["rt1"].cmd(
+ 'vtysh -c "conf t" -c "router ospf" -c "no fast-reroute ti-lfa node-protection"'
+ )
+
+ # check if we got back to the initial route table
+ router_compare_json_output(
+ "rt1",
+ "show ip route json",
+ "step3/show_ip_route_initial.ref",
+ )
+
+
+# Memory leak test template
+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/ospf_topo1/__init__.py b/tests/topotests/ospf_topo1/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/ospf_topo1/__init__.py
diff --git a/tests/topotests/ospf_topo1/r1/ospf6d.conf b/tests/topotests/ospf_topo1/r1/ospf6d.conf
new file mode 100644
index 0000000..ca3497b
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospf6d.conf
@@ -0,0 +1,13 @@
+!
+router ospf6
+ ospf6 router-id 10.0.255.1
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ interface r1-eth0 area 0.0.0.0
+ interface r1-eth1 area 0.0.0.0
+!
+int r1-eth1
+ ipv6 ospf6 dead-interval 10
+ ipv6 ospf6 hello-interval 2
+!
diff --git a/tests/topotests/ospf_topo1/r1/ospf6route.txt b/tests/topotests/ospf_topo1/r1/ospf6route.txt
new file mode 100644
index 0000000..d01511c
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospf6route.txt
@@ -0,0 +1,13 @@
+*N IA 2001:db8:1::/64 :: r1-eth0 00:02:11
+*N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
+ N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
+*N IA 2001:db8:3::/64 :: r1-eth1 00:02:11
+ N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r1-eth1 00:02:06
+*N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+*N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:06
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
+*N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
+ N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r1-eth1 00:02:04
diff --git a/tests/topotests/ospf_topo1/r1/ospf6route_down.txt b/tests/topotests/ospf_topo1/r1/ospf6route_down.txt
new file mode 100644
index 0000000..57113d0
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospf6route_down.txt
@@ -0,0 +1,5 @@
+*N IA 2001:db8:1::/64 :: r1-eth0 00:01:51
+*N IA 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
+ N E2 2001:db8:2::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
+*N IA 2001:db8:3::/64 :: r1-eth1 00:00:52
+ N E2 2001:db8:3::/64 fe80::281a:23ff:fe22:8a40 r1-eth1 00:00:52
diff --git a/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt
new file mode 100644
index 0000000..48e9209
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospf6route_ecmp.txt
@@ -0,0 +1,13 @@
+*N IA 2001:db8:1::/64 :: r1-eth0 00:06:13
+*N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08
+ N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r1-eth1 00:06:08
+*N IA 2001:db8:3::/64 :: r1-eth1 00:06:13
+ N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ fe80::e8bb:62ff:fee8:7022 r1-eth1
+*N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+*N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:08
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
+*N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
+ N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r1-eth1 00:06:07
diff --git a/tests/topotests/ospf_topo1/r1/ospfd.conf b/tests/topotests/ospf_topo1/r1/ospfd.conf
new file mode 100644
index 0000000..3b5aa19
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospfd.conf
@@ -0,0 +1,13 @@
+!
+router ospf
+ ospf router-id 10.0.255.1
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.1.0/24 area 0
+ network 10.0.3.0/24 area 0
+!
+int r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r1/ospfroute.txt b/tests/topotests/ospf_topo1/r1/ospfroute.txt
new file mode 100644
index 0000000..db64872
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospfroute.txt
@@ -0,0 +1,23 @@
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r1-eth1
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+N 10.0.10.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r1-eth1
+N IA 172.16.0.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r1-eth1
+N IA 172.16.1.0/24 [30] area: 0.0.0.0
+ via 10.0.3.1, r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r1-eth1
+R 10.0.255.3 [10] area: 0.0.0.0, ABR, ASBR
+ via 10.0.3.1, r1-eth1
+R 10.0.255.4 IA [20] area: 0.0.0.0, ASBR
+ via 10.0.3.1, r1-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r1/ospfroute_down.txt b/tests/topotests/ospf_topo1/r1/ospfroute_down.txt
new file mode 100644
index 0000000..5c07d81
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/ospfroute_down.txt
@@ -0,0 +1,13 @@
+============ OSPF network routing table ============
+N 10.0.1.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r1-eth1
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r1-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r1-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r1/zebra.conf b/tests/topotests/ospf_topo1/r1/zebra.conf
new file mode 100644
index 0000000..f6e8efe
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r1/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
+interface r1-eth1
+ ip address 10.0.3.2/24
+ ipv6 address 2001:db8:3::2/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_topo1/r2/ospf6d.conf b/tests/topotests/ospf_topo1/r2/ospf6d.conf
new file mode 100644
index 0000000..44047e1
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospf6d.conf
@@ -0,0 +1,17 @@
+!
+router ospf6
+ ospf6 router-id 10.0.255.2
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ interface r2-eth0 area 0.0.0.0
+ interface r2-eth1 area 0.0.0.0
+!
+int r2-eth0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+int r2-eth1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r2/ospf6route.txt b/tests/topotests/ospf_topo1/r2/ospf6route.txt
new file mode 100644
index 0000000..71c84d2
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospf6route.txt
@@ -0,0 +1,13 @@
+*N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
+ N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
+*N IA 2001:db8:2::/64 :: r2-eth0 00:03:39
+*N IA 2001:db8:3::/64 :: r2-eth1 00:03:34
+ N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r2-eth1 00:03:34
+ N E2 2001:db8:3::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+*N IA 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:100::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+*N IE 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:34
+ N E2 2001:db8:200::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
+*N IE 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
+ N E2 2001:db8:300::/64 fe80::50b7:d8ff:fe5f:8ff0 r2-eth1 00:03:32
diff --git a/tests/topotests/ospf_topo1/r2/ospf6route_down.txt b/tests/topotests/ospf_topo1/r2/ospf6route_down.txt
new file mode 100644
index 0000000..a1f0412
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospf6route_down.txt
@@ -0,0 +1,5 @@
+*N IA 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
+ N E2 2001:db8:1::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
+*N IA 2001:db8:2::/64 :: r2-eth0 00:07:17
+*N IA 2001:db8:3::/64 :: r2-eth1 00:06:27
+ N E2 2001:db8:3::/64 fe80::fc0b:daff:fe31:6791 r2-eth1 00:06:19
diff --git a/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt
new file mode 100644
index 0000000..0c06d23
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospf6route_ecmp.txt
@@ -0,0 +1,13 @@
+*N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04
+ N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r2-eth1 00:07:04
+*N IA 2001:db8:2::/64 :: r2-eth0 00:07:09
+*N IA 2001:db8:3::/64 :: r2-eth1 00:07:04
+ N E2 2001:db8:3::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ fe80::98cd:28ff:fe5e:3d93 r2-eth1
+*N IA 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:100::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+*N IE 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:04
+ N E2 2001:db8:200::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
+*N IE 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
+ N E2 2001:db8:300::/64 fe80::400f:dff:fe35:a1e7 r2-eth1 00:07:03
diff --git a/tests/topotests/ospf_topo1/r2/ospfd.conf b/tests/topotests/ospf_topo1/r2/ospfd.conf
new file mode 100644
index 0000000..1a7ccdf
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospfd.conf
@@ -0,0 +1,13 @@
+!
+router ospf
+ ospf router-id 10.0.255.2
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.2.0/24 area 0
+ network 10.0.3.0/24 area 0
+!
+int r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r2/ospfroute.txt b/tests/topotests/ospf_topo1/r2/ospfroute.txt
new file mode 100644
index 0000000..79b389b
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospfroute.txt
@@ -0,0 +1,23 @@
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+N 10.0.10.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r2-eth1
+N IA 172.16.0.0/24 [20] area: 0.0.0.0
+ via 10.0.3.1, r2-eth1
+N IA 172.16.1.0/24 [30] area: 0.0.0.0
+ via 10.0.3.1, r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r2-eth1
+R 10.0.255.3 [10] area: 0.0.0.0, ABR, ASBR
+ via 10.0.3.1, r2-eth1
+R 10.0.255.4 IA [20] area: 0.0.0.0, ASBR
+ via 10.0.3.1, r2-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r2/ospfroute_down.txt b/tests/topotests/ospf_topo1/r2/ospfroute_down.txt
new file mode 100644
index 0000000..b8411e1
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/ospfroute_down.txt
@@ -0,0 +1,13 @@
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r2-eth1
+N 10.0.2.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r2-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r2-eth1
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r2/zebra.conf b/tests/topotests/ospf_topo1/r2/zebra.conf
new file mode 100644
index 0000000..407416c
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r2/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface r2-eth0
+ ip address 10.0.2.1/24
+ ipv6 address 2001:db8:2::1/64
+!
+interface r2-eth1
+ ip address 10.0.3.3/24
+ ipv6 address 2001:db8:3::3/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_topo1/r3/ospf6d.conf b/tests/topotests/ospf_topo1/r3/ospf6d.conf
new file mode 100644
index 0000000..13ad9a7
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospf6d.conf
@@ -0,0 +1,22 @@
+!
+router ospf6
+ ospf6 router-id 10.0.255.3
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ interface r3-eth0 area 0.0.0.0
+ interface r3-eth1 area 0.0.0.0
+ interface r3-eth2 area 0.0.0.1
+!
+int r3-eth0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+int r3-eth1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+int r3-eth2
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r3/ospf6route.txt b/tests/topotests/ospf_topo1/r3/ospf6route.txt
new file mode 100644
index 0000000..69c99b4
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospf6route.txt
@@ -0,0 +1,12 @@
+*N IA 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
+ N E2 2001:db8:1::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
+*N IA 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
+ N E2 2001:db8:2::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
+*N IA 2001:db8:3::/64 :: r3-eth0 00:04:08
+ N E2 2001:db8:3::/64 fe80::b49b:4cff:fe80:4e87 r3-eth0 00:04:03
+ N E2 2001:db8:3::/64 fe80::b038:bcff:fe27:e2d6 r3-eth0 00:04:03
+*N IA 2001:db8:100::/64 :: r3-eth1 00:04:08
+*N IA 2001:db8:200::/64 :: r3-eth2 00:04:05
+ N E2 2001:db8:200::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
+*N IA 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
+ N E2 2001:db8:300::/64 fe80::78e0:deff:feb1:ec0 r3-eth2 00:04:00
diff --git a/tests/topotests/ospf_topo1/r3/ospf6route_down.txt b/tests/topotests/ospf_topo1/r3/ospf6route_down.txt
new file mode 100644
index 0000000..645ee0b
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospf6route_down.txt
@@ -0,0 +1,5 @@
+*N IA 2001:db8:100::/64 :: r3-eth1 00:08:06
+*N IA 2001:db8:200::/64 :: r3-eth2 00:08:04
+ N E2 2001:db8:200::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
+*N IA 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
+ N E2 2001:db8:300::/64 fe80::80a6:c3ff:fea9:88be r3-eth2 00:07:59
diff --git a/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt
new file mode 100644
index 0000000..ecd51be
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospf6route_ecmp.txt
@@ -0,0 +1,12 @@
+*N IA 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
+ N E2 2001:db8:1::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
+*N IA 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58
+ N E2 2001:db8:2::/64 fe80::e8bb:62ff:fee8:7022 r3-eth0 00:08:58
+*N IA 2001:db8:3::/64 :: r3-eth0 00:09:03
+ N E2 2001:db8:3::/64 fe80::98cd:28ff:fe5e:3d93 r3-eth0 00:08:58
+ fe80::e8bb:62ff:fee8:7022 r3-eth0
+*N IA 2001:db8:100::/64 :: r3-eth1 00:09:03
+*N IA 2001:db8:200::/64 :: r3-eth2 00:09:02
+ N E2 2001:db8:200::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
+*N IA 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
+ N E2 2001:db8:300::/64 fe80::d0dc:aff:fec5:5973 r3-eth2 00:08:57
diff --git a/tests/topotests/ospf_topo1/r3/ospfd.conf b/tests/topotests/ospf_topo1/r3/ospfd.conf
new file mode 100644
index 0000000..3b378c0
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospfd.conf
@@ -0,0 +1,18 @@
+!
+router ospf
+ ospf router-id 10.0.255.3
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 10.0.3.0/24 area 0
+ network 10.0.10.0/24 area 0
+ network 172.16.0.0/24 area 1
+!
+int r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+int r3-eth2
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r3/ospfroute.txt b/tests/topotests/ospf_topo1/r3/ospfroute.txt
new file mode 100644
index 0000000..c779906
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospfroute.txt
@@ -0,0 +1,23 @@
+============ OSPF network routing table ============
+N 10.0.1.0/24 [20] area: 0.0.0.0
+ via 10.0.3.2, r3-eth0
+N 10.0.2.0/24 [20] area: 0.0.0.0
+ via 10.0.3.3, r3-eth0
+N 10.0.3.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth0
+N 10.0.10.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+N 172.16.0.0/24 [10] area: 0.0.0.1
+ directly attached to r3-eth2
+N 172.16.1.0/24 [20] area: 0.0.0.1
+ via 172.16.0.1, r3-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.1 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.2, r3-eth0
+R 10.0.255.2 [10] area: 0.0.0.0, ASBR
+ via 10.0.3.3, r3-eth0
+R 10.0.255.4 [10] area: 0.0.0.1, ASBR
+ via 172.16.0.1, r3-eth2
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r3/ospfroute_down.txt b/tests/topotests/ospf_topo1/r3/ospfroute_down.txt
new file mode 100644
index 0000000..692a74a
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/ospfroute_down.txt
@@ -0,0 +1,13 @@
+============ OSPF network routing table ============
+N 10.0.10.0/24 [10] area: 0.0.0.0
+ directly attached to r3-eth1
+N 172.16.0.0/24 [10] area: 0.0.0.1
+ directly attached to r3-eth2
+N 172.16.1.0/24 [20] area: 0.0.0.1
+ via 172.16.0.1, r3-eth2
+
+============ OSPF router routing table =============
+R 10.0.255.4 [10] area: 0.0.0.1, ASBR
+ via 172.16.0.1, r3-eth2
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r3/zebra.conf b/tests/topotests/ospf_topo1/r3/zebra.conf
new file mode 100644
index 0000000..a635a88
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r3/zebra.conf
@@ -0,0 +1,15 @@
+!
+interface r3-eth0
+ ip address 10.0.3.1/24
+ ipv6 address 2001:db8:3::1/64
+!
+interface r3-eth1
+ ip address 10.0.10.1/24
+ ipv6 address 2001:db8:100::1/64
+!
+interface r3-eth2
+ ip address 172.16.0.2/24
+ ipv6 address 2001:db8:200::2/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_topo1/r4/ospf6d.conf b/tests/topotests/ospf_topo1/r4/ospf6d.conf
new file mode 100644
index 0000000..f9bde0e
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospf6d.conf
@@ -0,0 +1,17 @@
+!
+router ospf6
+ ospf6 router-id 10.0.255.4
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ interface r4-eth0 area 0.0.0.1
+ interface r4-eth1 area 0.0.0.1
+!
+int r4-eth0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+int r4-eth1
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r4/ospf6route.txt b/tests/topotests/ospf_topo1/r4/ospf6route.txt
new file mode 100644
index 0000000..3a4f5ef
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospf6route.txt
@@ -0,0 +1,13 @@
+*N IE 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:1::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+*N IE 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:2::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+*N IE 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:3::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+*N IE 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+ N E2 2001:db8:100::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+*N IA 2001:db8:200::/64 :: r4-eth0 00:04:30
+ N E2 2001:db8:200::/64 fe80::987b:baff:fe8a:c864 r4-eth0 00:04:25
+*N IA 2001:db8:300::/64 :: r4-eth1 00:04:30
diff --git a/tests/topotests/ospf_topo1/r4/ospf6route_down.txt b/tests/topotests/ospf_topo1/r4/ospf6route_down.txt
new file mode 100644
index 0000000..165f8db
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospf6route_down.txt
@@ -0,0 +1,5 @@
+*N IE 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
+ N E2 2001:db8:100::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
+*N IA 2001:db8:200::/64 :: r4-eth0 00:01:50
+ N E2 2001:db8:200::/64 fe80::b44b:a1ff:fe48:3d69 r4-eth0 00:01:45
+*N IA 2001:db8:300::/64 :: r4-eth1 00:01:50
diff --git a/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt b/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt
new file mode 100644
index 0000000..d0d72a8
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospf6route_ecmp.txt
@@ -0,0 +1,12 @@
+*N IE 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:1::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+*N IE 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:2::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+*N IE 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:3::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+*N IE 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+ N E2 2001:db8:100::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+*N IA 2001:db8:200::/64 :: r4-eth0 00:09:17
+ N E2 2001:db8:200::/64 fe80::78fe:fcff:fe51:9afc r4-eth0 00:09:13
+*N IA 2001:db8:300::/64 :: r4-eth1 00:09:18
diff --git a/tests/topotests/ospf_topo1/r4/ospfd.conf b/tests/topotests/ospf_topo1/r4/ospfd.conf
new file mode 100644
index 0000000..52d2932
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospfd.conf
@@ -0,0 +1,13 @@
+!
+router ospf
+ ospf router-id 10.0.255.4
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 172.16.0.0/24 area 1
+ network 172.16.1.0/24 area 1
+!
+int r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
diff --git a/tests/topotests/ospf_topo1/r4/ospfroute.txt b/tests/topotests/ospf_topo1/r4/ospfroute.txt
new file mode 100644
index 0000000..b582ef0
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospfroute.txt
@@ -0,0 +1,23 @@
+============ OSPF network routing table ============
+N IA 10.0.1.0/24 [30] area: 0.0.0.1
+ via 172.16.0.2, r4-eth0
+N IA 10.0.2.0/24 [30] area: 0.0.0.1
+ via 172.16.0.2, r4-eth0
+N IA 10.0.3.0/24 [20] area: 0.0.0.1
+ via 172.16.0.2, r4-eth0
+N IA 10.0.10.0/24 [20] area: 0.0.0.1
+ via 172.16.0.2, r4-eth0
+N 172.16.0.0/24 [10] area: 0.0.0.1
+ directly attached to r4-eth0
+N 172.16.1.0/24 [10] area: 0.0.0.1
+ directly attached to r4-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.1 IA [20] area: 0.0.0.1, ASBR
+ via 172.16.0.2, r4-eth0
+R 10.0.255.2 IA [20] area: 0.0.0.1, ASBR
+ via 172.16.0.2, r4-eth0
+R 10.0.255.3 [10] area: 0.0.0.1, ABR, ASBR
+ via 172.16.0.2, r4-eth0
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r4/ospfroute_down.txt b/tests/topotests/ospf_topo1/r4/ospfroute_down.txt
new file mode 100644
index 0000000..b0bd0ee
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/ospfroute_down.txt
@@ -0,0 +1,13 @@
+============ OSPF network routing table ============
+N IA 10.0.10.0/24 [20] area: 0.0.0.1
+ via 172.16.0.2, r4-eth0
+N 172.16.0.0/24 [10] area: 0.0.0.1
+ directly attached to r4-eth0
+N 172.16.1.0/24 [10] area: 0.0.0.1
+ directly attached to r4-eth1
+
+============ OSPF router routing table =============
+R 10.0.255.3 [10] area: 0.0.0.1, ABR, ASBR
+ via 172.16.0.2, r4-eth0
+
+============ OSPF external routing table ===========
diff --git a/tests/topotests/ospf_topo1/r4/zebra.conf b/tests/topotests/ospf_topo1/r4/zebra.conf
new file mode 100644
index 0000000..39ecbb2
--- /dev/null
+++ b/tests/topotests/ospf_topo1/r4/zebra.conf
@@ -0,0 +1,11 @@
+!
+interface r4-eth0
+ ip address 172.16.0.1/24
+ ipv6 address 2001:db8:200::1/64
+!
+interface r4-eth1
+ ip address 172.16.1.100/24
+ ipv6 address 2001:db8:300::100/64
+!
+ip forwarding
+!
diff --git a/tests/topotests/ospf_topo1/test_ospf_topo1.dot b/tests/topotests/ospf_topo1/test_ospf_topo1.dot
new file mode 100644
index 0000000..469a7ea
--- /dev/null
+++ b/tests/topotests/ospf_topo1/test_ospf_topo1.dot
@@ -0,0 +1,104 @@
+## 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 ospf_topo1 {
+ label="ospf topo1";
+
+ # Routers
+ r1 [
+ label="r1\nrtr-id 10.0.255.1/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r2 [
+ label="r2\nrtr-id 10.0.255.2/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r3 [
+ label="r3\nrtr-id 10.0.255.3/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+ r4 [
+ label="r4\nrtr-id 10.0.255.4/32",
+ shape=doubleoctagon,
+ fillcolor="#f08080",
+ style=filled,
+ ];
+
+ # Switches
+ s1 [
+ label="s1\n10.0.1.0/24\n2001:db8:1::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s2 [
+ label="s2\n10.0.2.0/24\n2001:db8:2::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s3 [
+ label="s3\n10.0.3.0/24\n2001:db8:3::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s4 [
+ label="s4\n10.0.10.0/24\n2001:db8:100::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s5 [
+ label="s5\n172.16.0.0/24\n2001:db8:200::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ s6 [
+ label="s6\n172.16.1.0/24\n2001:db8:300::/64",
+ shape=oval,
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ subgraph cluster0 {
+ label="area 0"
+
+ r1 -- s1 [label="eth0\n.1\n::1"];
+ r1 -- s3 [label="eth1\n.2\n::2"];
+
+ r2 -- s2 [label="eth0\n.1\n::1"];
+ r2 -- s3 [label="eth1\n.3\n::3"];
+
+ r3 -- s3 [label="eth0\n.1\n::1"];
+ r3 -- s4 [label="eth1\n.1\n::1"];
+ }
+
+ subgraph cluster1 {
+ label="area 1"
+
+ r3 -- s5 [label="eth2\n.2\n::2"];
+
+ r4 -- s5 [label="eth0\n.1\n::1"];
+ r4 -- s6 [label="eth1\n.100\n::100"];
+ }
+}
diff --git a/tests/topotests/ospf_topo1/test_ospf_topo1.jpg b/tests/topotests/ospf_topo1/test_ospf_topo1.jpg
new file mode 100644
index 0000000..1e93170
--- /dev/null
+++ b/tests/topotests/ospf_topo1/test_ospf_topo1.jpg
Binary files differ
diff --git a/tests/topotests/ospf_topo1/test_ospf_topo1.py b/tests/topotests/ospf_topo1/test_ospf_topo1.py
new file mode 100644
index 0000000..37facfc
--- /dev/null
+++ b/tests/topotests/ospf_topo1/test_ospf_topo1.py
@@ -0,0 +1,598 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_ospf_topo1.py: Test the FRR OSPF routing daemon.
+"""
+
+import os
+import re
+import sys
+from functools import partial
+from time import sleep
+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.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 5):
+ tgen.add_router("r{}".format(routern))
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2 and 3
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+ # Create empty netowrk for router3
+ switch = tgen.add_switch("s4")
+ switch.add_link(tgen.gears["r3"])
+
+ # Interconect router 3 and 4
+ switch = tgen.add_switch("s5")
+ switch.add_link(tgen.gears["r3"])
+ switch.add_link(tgen.gears["r4"])
+
+ # Create a empty network for router 4
+ switch = tgen.add_switch("s6")
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ ospf6_config = "ospf6d.conf"
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/{}".format(rname, ospf6_config))
+ )
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_wait_protocol_convergence():
+ "Wait for OSPFv2/OSPFv3 to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for protocols to converge")
+
+ def expect_ospfv2_neighbor_full(router, neighbor):
+ "Wait until OSPFv2 convergence."
+ logger.info("waiting OSPFv2 router '{}'".format(router))
+
+ def run_command_and_expect():
+ """
+ Function that runs command and expect the following outcomes:
+ * Full/DR
+ * Full/DROther
+ * Full/Backup
+ """
+ result = tgen.gears[router].vtysh_cmd(
+ "show ip ospf neighbor json", isjson=True
+ )
+ if (
+ topotest.json_cmp(
+ result, {"neighbors": {neighbor: [{"converged": "Full"}]}}
+ )
+ is None
+ ):
+ return None
+
+ if (
+ topotest.json_cmp(
+ result, {"neighbors": {neighbor: [{"converged": "Full"}]}}
+ )
+ is None
+ ):
+ return None
+
+ return topotest.json_cmp(
+ result, {"neighbors": {neighbor: [{"converged": "Full"}]}}
+ )
+
+ _, result = topotest.run_and_expect(
+ run_command_and_expect, None, count=130, wait=1
+ )
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ def expect_ospfv3_neighbor_full(router, neighbor):
+ "Wait until OSPFv3 convergence."
+ logger.info("waiting OSPFv3 router '{}'".format(router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ipv6 ospf6 neighbor json",
+ {"neighbors": [{"neighborId": neighbor, "state": "Full"}]},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ # Wait for OSPFv2 convergence
+ expect_ospfv2_neighbor_full("r1", "10.0.255.2")
+ expect_ospfv2_neighbor_full("r1", "10.0.255.3")
+ expect_ospfv2_neighbor_full("r2", "10.0.255.1")
+ expect_ospfv2_neighbor_full("r2", "10.0.255.3")
+ expect_ospfv2_neighbor_full("r3", "10.0.255.1")
+ expect_ospfv2_neighbor_full("r3", "10.0.255.2")
+ expect_ospfv2_neighbor_full("r3", "10.0.255.4")
+ expect_ospfv2_neighbor_full("r4", "10.0.255.3")
+
+ # Wait for OSPFv3 convergence
+ expect_ospfv3_neighbor_full("r1", "10.0.255.2")
+ expect_ospfv3_neighbor_full("r1", "10.0.255.3")
+ expect_ospfv3_neighbor_full("r2", "10.0.255.1")
+ expect_ospfv3_neighbor_full("r2", "10.0.255.3")
+ expect_ospfv3_neighbor_full("r3", "10.0.255.1")
+ expect_ospfv3_neighbor_full("r3", "10.0.255.2")
+ expect_ospfv3_neighbor_full("r3", "10.0.255.4")
+ expect_ospfv3_neighbor_full("r4", "10.0.255.3")
+
+
+def compare_show_ipv6_ospf6(rname, expected):
+ """
+ Calls 'show ipv6 ospf6 route' for router `rname` and compare the obtained
+ result with the expected output.
+ """
+ tgen = get_topogen()
+ current = tgen.gears[rname].vtysh_cmd("show ipv6 ospf6 route")
+
+ # Remove the link addresses
+ current = re.sub(r"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", current)
+ expected = re.sub(r"fe80::[^ ]+", "fe80::xxxx:xxxx:xxxx:xxxx", expected)
+
+ # Remove the time
+ current = re.sub(r"\d+:\d{2}:\d{2}", "", current)
+ expected = re.sub(r"\d+:\d{2}:\d{2}", "", expected)
+
+ return topotest.difflines(
+ topotest.normalize_text(current),
+ topotest.normalize_text(expected),
+ title1="Current output",
+ title2="Expected output",
+ )
+
+
+def test_ospf_convergence():
+ "Test OSPF daemon convergence"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospfroute.txt".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ topotest.router_output_cmp, rnode, "show ip ospf route", expected
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=160, wait=0.5)
+ assert result, "OSPF did not converge on {}:\n{}".format(router, diff)
+
+
+def test_ospf_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name)
+
+ routes = topotest.ip4_route(router)
+ expected = {
+ "10.0.1.0/24": {},
+ "10.0.2.0/24": {},
+ "10.0.3.0/24": {},
+ "10.0.10.0/24": {},
+ "172.16.0.0/24": {},
+ "172.16.1.0/24": {},
+ }
+ assertmsg = 'OSPF IPv4 route mismatch in router "{}"'.format(router.name)
+ assert topotest.json_cmp(routes, expected) is None, assertmsg
+
+
+def test_ospf6_convergence():
+ "Test OSPF6 daemon convergence"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ ospf6route_file = "{}/ospf6route_ecmp.txt"
+ for rnum in range(1, 5):
+ router = "r{}".format(rnum)
+
+ logger.info('Waiting for router "%s" IPv6 OSPF convergence', router)
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, ospf6route_file.format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_ospf6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3)
+ if (not result) and (rnum == 1):
+ # Didn't match the new ECMP version - try the old pre-ECMP format
+ ospf6route_file = "{}/ospf6route.txt"
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, ospf6route_file.format(router))
+ expected = open(reffile).read()
+
+ test_func = partial(compare_show_ipv6_ospf6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=1, wait=3)
+ if not result:
+ # Didn't match the old version - switch back to new ECMP version
+ # and fail
+ ospf6route_file = "{}/ospf6route_ecmp.txt"
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, ospf6route_file.format(router))
+ expected = open(reffile).read()
+
+ test_func = partial(compare_show_ipv6_ospf6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=1, wait=3)
+
+ assert result, "OSPF6 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_ospf6_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv6 kernel routes in "%s"', router.name)
+
+ def _routes_in_fib6():
+ routes = topotest.ip6_route(router)
+ expected = {
+ "2001:db8:1::/64": {},
+ "2001:db8:2::/64": {},
+ "2001:db8:3::/64": {},
+ "2001:db8:100::/64": {},
+ "2001:db8:200::/64": {},
+ "2001:db8:300::/64": {},
+ }
+ logger.info("Routes:")
+ logger.info(routes)
+ logger.info(topotest.json_cmp(routes, expected))
+ logger.info("ENd:")
+ return topotest.json_cmp(routes, expected)
+
+ _, result = topotest.run_and_expect(_routes_in_fib6, None, count=20, wait=1)
+
+ assertmsg = 'OSPF IPv6 route mismatch in router "{}"'.format(router.name)
+ assert result is None, assertmsg
+
+
+def test_ospf_json():
+ "Test 'show ip ospf json' output for coherency."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rnum in range(1, 5):
+ router = tgen.gears["r{}".format(rnum)]
+ logger.info(router.vtysh_cmd("show ip ospf database"))
+ logger.info('Comparing router "%s" "show ip ospf json" output', router.name)
+ expected = {
+ "routerId": "10.0.255.{}".format(rnum),
+ "tosRoutesOnly": True,
+ "rfc2328Conform": True,
+ "spfScheduleDelayMsecs": 0,
+ "holdtimeMinMsecs": 50,
+ "holdtimeMaxMsecs": 5000,
+ "lsaMinIntervalMsecs": 5000,
+ "lsaMinArrivalMsecs": 1000,
+ "writeMultiplier": 20,
+ "refreshTimerMsecs": 10000,
+ "asbrRouter": "injectingExternalRoutingInformation",
+ "attachedAreaCounter": 1,
+ "areas": {},
+ }
+ # Area specific additional checks
+ if router.name == "r1" or router.name == "r2" or router.name == "r3":
+ expected["areas"]["0.0.0.0"] = {
+ "areaIfActiveCounter": 2,
+ "areaIfTotalCounter": 2,
+ "authentication": "authenticationNone",
+ "backbone": True,
+ "lsaAsbrNumber": 1,
+ "lsaNetworkNumber": 1,
+ "lsaNssaNumber": 0,
+ "lsaNumber": 7,
+ "lsaOpaqueAreaNumber": 0,
+ "lsaOpaqueLinkNumber": 0,
+ "lsaRouterNumber": 3,
+ "lsaSummaryNumber": 2,
+ "nbrFullAdjacentCounter": 2,
+ }
+ if router.name == "r3" or router.name == "r4":
+ expected["areas"]["0.0.0.1"] = {
+ "areaIfActiveCounter": 1,
+ "areaIfTotalCounter": 1,
+ "authentication": "authenticationNone",
+ "lsaAsbrNumber": 2,
+ "lsaNetworkNumber": 1,
+ "lsaNssaNumber": 0,
+ "lsaNumber": 9,
+ "lsaOpaqueAreaNumber": 0,
+ "lsaOpaqueLinkNumber": 0,
+ "lsaRouterNumber": 2,
+ "lsaSummaryNumber": 4,
+ "nbrFullAdjacentCounter": 1,
+ }
+ # r4 has more interfaces for area 0.0.0.1
+ if router.name == "r4":
+ expected["areas"]["0.0.0.1"].update(
+ {
+ "areaIfActiveCounter": 2,
+ "areaIfTotalCounter": 2,
+ }
+ )
+
+ # router 3 has an additional area
+ if router.name == "r3":
+ expected["attachedAreaCounter"] = 2
+
+ output = router.vtysh_cmd("show ip ospf json", isjson=True)
+ result = topotest.json_cmp(output, expected)
+ assert result is None, '"{}" JSON output mismatches the expected result'.format(
+ router.name
+ )
+
+
+def test_ospf_link_down():
+ "Test OSPF convergence after a link goes down"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ # Simulate a network down event on router3 switch3 interface.
+ router3 = tgen.gears["r3"]
+ router3.peer_link_enable("r3-eth0", False)
+
+ # Expect convergence on all routers
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence after link failure', router)
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospfroute_down.txt".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 80 seconds.
+ test_func = partial(
+ topotest.router_output_cmp, rnode, "show ip ospf route", expected
+ )
+ result, diff = topotest.run_and_expect(test_func, "", count=140, wait=0.5)
+ assert result, "OSPF did not converge on {}:\n{}".format(router, diff)
+
+
+def test_ospf_link_down_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info(
+ 'Checking OSPF IPv4 kernel routes in "%s" after link down', router.name
+ )
+
+ routes = topotest.ip4_route(router)
+ expected = {
+ "10.0.1.0/24": {},
+ "10.0.2.0/24": {},
+ "10.0.3.0/24": {},
+ "10.0.10.0/24": {},
+ "172.16.0.0/24": {},
+ "172.16.1.0/24": {},
+ }
+ if router.name == "r1" or router.name == "r2":
+ expected.update(
+ {
+ "10.0.10.0/24": None,
+ "172.16.0.0/24": None,
+ "172.16.1.0/24": None,
+ }
+ )
+ elif router.name == "r3" or router.name == "r4":
+ expected.update(
+ {
+ "10.0.1.0/24": None,
+ "10.0.2.0/24": None,
+ }
+ )
+ # Route '10.0.3.0' is no longer available for r4 since it is down.
+ if router.name == "r4":
+ expected.update(
+ {
+ "10.0.3.0/24": None,
+ }
+ )
+ assertmsg = 'OSPF IPv4 route mismatch in router "{}" after link down'.format(
+ router.name
+ )
+ count = 0
+ not_found = True
+ while not_found and count < 10:
+ not_found = topotest.json_cmp(routes, expected)
+ if not_found:
+ sleep(1)
+ routes = topotest.ip4_route(router)
+ count += 1
+ else:
+ not_found = False
+ break
+ assert not_found is False, assertmsg
+
+
+def test_ospf6_link_down():
+ "Test OSPF6 daemon convergence after link goes down"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for rnum in range(1, 5):
+ router = "r{}".format(rnum)
+
+ logger.info(
+ 'Waiting for router "%s" IPv6 OSPF convergence after link down', router
+ )
+
+ # Load expected results from the command
+ reffile = os.path.join(CWD, "{}/ospf6route_down.txt".format(router))
+ expected = open(reffile).read()
+
+ # Run test function until we get an result. Wait at most 60 seconds.
+ test_func = partial(compare_show_ipv6_ospf6, router, expected)
+ result, diff = topotest.run_and_expect(test_func, "", count=25, wait=3)
+ assert result, "OSPF6 did not converge on {}:\n{}".format(router, diff)
+
+
+def test_ospf6_link_down_kernel_route():
+ "Test OSPF kernel route installation"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info(
+ 'Checking OSPF IPv6 kernel routes in "%s" after link down', router.name
+ )
+
+ routes = topotest.ip6_route(router)
+ expected = {
+ "2001:db8:1::/64": {},
+ "2001:db8:2::/64": {},
+ "2001:db8:3::/64": {},
+ "2001:db8:100::/64": {},
+ "2001:db8:200::/64": {},
+ "2001:db8:300::/64": {},
+ }
+ if router.name == "r1" or router.name == "r2":
+ expected.update(
+ {
+ "2001:db8:100::/64": None,
+ "2001:db8:200::/64": None,
+ "2001:db8:300::/64": None,
+ }
+ )
+ elif router.name == "r3" or router.name == "r4":
+ expected.update(
+ {
+ "2001:db8:1::/64": None,
+ "2001:db8:2::/64": None,
+ }
+ )
+ # Route '2001:db8:3::/64' is no longer available for r4 since it is down.
+ if router.name == "r4":
+ expected.update(
+ {
+ "2001:db8:3::/64": None,
+ }
+ )
+ assertmsg = 'OSPF IPv6 route mismatch in router "{}" after link down'.format(
+ router.name
+ )
+ count = 0
+ not_found = True
+ while not_found and count < 10:
+ not_found = topotest.json_cmp(routes, expected)
+ if not_found:
+ sleep(1)
+ routes = topotest.ip6_route(router)
+ count += 1
+ else:
+ not_found = False
+ break
+
+ assert not_found is False, assertmsg
+
+
+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/ospf_unnumbered/r1/ospf-route.json b/tests/topotests/ospf_unnumbered/r1/ospf-route.json
new file mode 100644
index 0000000..6beb7e9
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r1/ospf-route.json
@@ -0,0 +1 @@
+{ "10.0.1.1\/32": { "routeType": "N", "cost": 10, "area": "0.0.0.0", "nexthops": [ { "ip": " ", "directly attached to": "r1-eth0" } ] }, "10.0.20.1\/32": { "routeType": "N", "cost": 20, "area": "0.0.0.0", "nexthops": [ { "ip": "10.0.3.2", "via": "r1-eth1" } ] }, "10.0.255.2": { "routeType": "R ", "cost": 10, "area": "0.0.0.0", "routerType": "asbr", "nexthops": [ { "ip": "10.0.3.2", "via": "r1-eth1" } ] } }
diff --git a/tests/topotests/ospf_unnumbered/r1/ospfd.conf b/tests/topotests/ospf_unnumbered/r1/ospfd.conf
new file mode 100644
index 0000000..65843cb
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r1/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface r1-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 10.0.255.1
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 0.0.0.0/0 area 0
+!
diff --git a/tests/topotests/ospf_unnumbered/r1/v4_route.json b/tests/topotests/ospf_unnumbered/r1/v4_route.json
new file mode 100644
index 0000000..76c6396
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r1/v4_route.json
@@ -0,0 +1,84 @@
+{
+ "10.0.1.1\/32":[
+ {
+ "prefix":"10.0.1.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":9,
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.4\/32":[
+ {
+ "prefix":"10.0.3.4\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.20.1\/32":[
+ {
+ "prefix":"10.0.20.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":11,
+ "fib":true,
+ "ip":"10.0.3.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_unnumbered/r1/zebra.conf b/tests/topotests/ospf_unnumbered/r1/zebra.conf
new file mode 100644
index 0000000..d96d970
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r1/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r1-eth0
+ ip address 10.0.1.1/32
+!
+interface r1-eth1
+ ip address 10.0.3.4/32
+!
diff --git a/tests/topotests/ospf_unnumbered/r2/ospf-route.json b/tests/topotests/ospf_unnumbered/r2/ospf-route.json
new file mode 100644
index 0000000..3cfd255
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r2/ospf-route.json
@@ -0,0 +1 @@
+{ "10.0.1.1\/32": { "routeType": "N", "cost": 20, "area": "0.0.0.0", "nexthops": [ { "ip": "10.0.3.4", "via": "r2-eth1" } ] }, "10.0.20.1\/32": { "routeType": "N", "cost": 10, "area": "0.0.0.0", "nexthops": [ { "ip": " ", "directly attached to": "r2-eth0" } ] }, "10.0.255.1": { "routeType": "R ", "cost": 10, "area": "0.0.0.0", "routerType": "asbr", "nexthops": [ { "ip": "10.0.3.4", "via": "r2-eth1" } ] } }
diff --git a/tests/topotests/ospf_unnumbered/r2/ospfd.conf b/tests/topotests/ospf_unnumbered/r2/ospfd.conf
new file mode 100644
index 0000000..b032f1a
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r2/ospfd.conf
@@ -0,0 +1,13 @@
+!
+interface r2-eth1
+ ip ospf network point-to-point
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf router-id 10.0.255.2
+ redistribute kernel
+ redistribute connected
+ redistribute static
+ network 0.0.0.0/0 area 0
+!
diff --git a/tests/topotests/ospf_unnumbered/r2/v4_route.json b/tests/topotests/ospf_unnumbered/r2/v4_route.json
new file mode 100644
index 0000000..1638536
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r2/v4_route.json
@@ -0,0 +1,84 @@
+{
+ "10.0.1.1\/32":[
+ {
+ "prefix":"10.0.1.1\/32",
+ "protocol":"ospf",
+ "selected":true,
+ "destSelected":true,
+ "distance":110,
+ "metric":20,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":11,
+ "fib":true,
+ "ip":"10.0.3.4",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth1",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ }
+ ],
+ "10.0.3.2\/32":[
+ {
+ "prefix":"10.0.3.2\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.20.1\/32":[
+ {
+ "prefix":"10.0.20.1\/32",
+ "protocol":"ospf",
+ "distance":110,
+ "metric":10,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":9,
+ "ip":"0.0.0.0",
+ "afi":"ipv4",
+ "interfaceName":"r2-eth0",
+ "active":true,
+ "onLink":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.20.1\/32",
+ "protocol":"connected",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r2-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/ospf_unnumbered/r2/zebra.conf b/tests/topotests/ospf_unnumbered/r2/zebra.conf
new file mode 100644
index 0000000..f9dd2c4
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.20.1/32
+!
+interface r2-eth1
+ ip address 10.0.3.2/32
+!
diff --git a/tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py b/tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py
new file mode 100644
index 0000000..a9640ad
--- /dev/null
+++ b/tests/topotests/ospf_unnumbered/test_ospf_unnumbered.py
@@ -0,0 +1,165 @@
+#!/usr/bin/env python
+
+#
+# test_ospf_unnumbered.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc
+# Donald Sharp
+#
+# 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_ospf_unnumbered.py: Test the OSPF unnumbered.
+"""
+
+import os
+import sys
+from functools import partial
+import pytest
+import json
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+# Required to instantiate the topology builder class.
+
+pytestmark = [pytest.mark.ospfd]
+
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Create 4 routers
+ for routern in range(1, 3):
+ tgen.add_router("r{}".format(routern))
+
+ # Create a empty network for router 1
+ switch = tgen.add_switch("s1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Create a empty network for router 2
+ switch = tgen.add_switch("s2")
+ switch.add_link(tgen.gears["r2"])
+
+ # Interconect router 1, 2
+ switch = tgen.add_switch("s3")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+
+ # What is this? OSPF Unnumbered depends on the rp_filter
+ # being set appropriately( HA! )
+ # Effectively we are putting different /32's on the interface
+ # the multicast packet delivery is somewhat controlled by
+ # the rp_filter. Setting it to '0' allows the OS to pass
+ # up the mcast packet not destined for the local routers
+ # network.
+ topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.r1-eth1.rp_filter", 0)
+ topotest.sysctl_assure(tgen.net["r1"], "net.ipv4.conf.all.rp_filter", 0)
+ topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.r2-eth1.rp_filter", 0)
+ topotest.sysctl_assure(tgen.net["r2"], "net.ipv4.conf.all.rp_filter", 0)
+
+ # Initialize all routers.
+ tgen.start_router()
+ # tgen.mininet_cli()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ospf_convergence():
+ "Test OSPF daemon convergence and that we have received the ospf routes"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ for router, rnode in tgen.routers().items():
+ logger.info('Waiting for router "%s" convergence', router)
+
+ json_file = "{}/{}/ospf-route.json".format(CWD, router)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, rnode, "show ip ospf route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=160, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(router)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_ospf_kernel_route():
+ "Test OSPF kernel route installation and we have the onlink success"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ rlist = tgen.routers().values()
+ for router in rlist:
+ logger.info('Checking OSPF IPv4 kernel routes in "%s"', router.name)
+
+ json_file = "{}/{}/v4_route.json".format(CWD, router.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=0.5)
+ assertmsg = '"{}" JSON output mistmatches'.format(router)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+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/ospfapi/ctester.py b/tests/topotests/ospfapi/ctester.py
new file mode 100755
index 0000000..8fd202a
--- /dev/null
+++ b/tests/topotests/ospfapi/ctester.py
@@ -0,0 +1,171 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# January 17 2022, Christian Hopps <chopps@labn.net>
+#
+# Copyright 2022, LabN Consulting, L.L.C.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+import argparse
+import asyncio
+import logging
+import os
+import sys
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+sys.path[0:0] = [CLIENTDIR]
+
+import ospfclient as api # pylint: disable=E0401 # noqa: E402
+
+
+async def do_monitor(c, args):
+ cv = asyncio.Condition()
+
+ async def cb(new_router_id, _):
+ assert new_router_id == c.router_id
+ logging.info("NEW ROUTER ID: %s", new_router_id)
+ sys.stdout.flush()
+ async with cv:
+ cv.notify_all()
+
+ logging.debug("API using monitor router ID callback")
+ await c.monitor_router_id(callback=cb)
+
+ for check in args.monitor:
+ logging.info("Waiting for %s", check)
+
+ while True:
+ async with cv:
+ got = c.router_id
+ if str(check) == str(got):
+ break
+ logging.debug("expected '%s' != '%s'\nwaiting on notify", check, got)
+ await cv.wait()
+
+ logging.info("SUCCESS: %s", check)
+ print("SUCCESS: {}".format(check))
+ sys.stdout.flush()
+
+
+async def do_wait(c, args):
+ cv = asyncio.Condition()
+
+ async def cb(added, removed):
+ logging.debug("callback: added: %s removed: %s", added, removed)
+ sys.stdout.flush()
+ async with cv:
+ cv.notify_all()
+
+ logging.debug("API using monitor reachable callback")
+ await c.monitor_reachable(callback=cb)
+
+ for w in args.wait:
+ check = ",".join(sorted(list(w.split(","))))
+ logging.info("Waiting for %s", check)
+
+ while True:
+ async with cv:
+ got = ",".join(sorted([str(x) for x in c.reachable_routers]))
+ if check == got:
+ break
+ logging.debug("expected '%s' != '%s'\nwaiting on notify", check, got)
+ await cv.wait()
+
+ logging.info("SUCCESS: %s", check)
+ print("SUCCESS: {}".format(check))
+ sys.stdout.flush()
+
+
+async def async_main(args):
+ c = api.OspfOpaqueClient(args.server)
+ await c.connect()
+ if sys.version_info[1] > 6:
+ asyncio.create_task(c._handle_msg_loop()) # pylint: disable=W0212
+ else:
+ asyncio.get_event_loop().create_task(
+ c._handle_msg_loop() # pylint: disable=W0212
+ )
+
+ if args.monitor:
+ await do_monitor(c, args)
+ if args.wait:
+ await do_wait(c, args)
+ return 0
+
+
+def main(*args):
+ ap = argparse.ArgumentParser(args)
+ ap.add_argument(
+ "--monitor", action="append", help="monitor and wait for this router ID"
+ )
+ ap.add_argument("--server", default="localhost", help="OSPF API server")
+ ap.add_argument(
+ "--wait", action="append", help="wait for comma-sep set of reachable routers"
+ )
+ ap.add_argument("-v", "--verbose", action="store_true", help="be verbose")
+ args = ap.parse_args()
+
+ level = logging.DEBUG if args.verbose else logging.INFO
+ logging.basicConfig(
+ level=level, format="%(asctime)s %(levelname)s: TESTER: %(name)s: %(message)s"
+ )
+
+ # We need to flush this output to stdout right away
+ h = logging.StreamHandler(sys.stdout)
+ h.flush = sys.stdout.flush
+ f = logging.Formatter("%(asctime)s %(name)s: %(levelname)s: %(message)s")
+ h.setFormatter(f)
+ logger = logging.getLogger("ospfclient")
+ logger.addHandler(h)
+ logger.propagate = False
+
+ logging.info("ctester: starting")
+ sys.stdout.flush()
+
+ status = 3
+ try:
+ if sys.version_info[1] > 6:
+ status = asyncio.run(async_main(args))
+ else:
+ loop = asyncio.get_event_loop()
+ try:
+ status = loop.run_until_complete(async_main(args))
+ finally:
+ loop.close()
+ except KeyboardInterrupt:
+ logging.info("Exiting, received KeyboardInterrupt in main")
+ except Exception as error:
+ logging.info("Exiting, unexpected exception %s", error, exc_info=True)
+ else:
+ logging.info("api: clean exit")
+
+ return status
+
+
+if __name__ == "__main__":
+ exit_status = main()
+ sys.exit(exit_status)
diff --git a/tests/topotests/ospfapi/lib b/tests/topotests/ospfapi/lib
new file mode 120000
index 0000000..dc598c5
--- /dev/null
+++ b/tests/topotests/ospfapi/lib
@@ -0,0 +1 @@
+../lib \ No newline at end of file
diff --git a/tests/topotests/ospfapi/r1/ospfd.conf b/tests/topotests/ospfapi/r1/ospfd.conf
new file mode 100644
index 0000000..338691b
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r1-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 1.0.0.0
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r1/zebra.conf b/tests/topotests/ospfapi/r1/zebra.conf
new file mode 100644
index 0000000..7451591
--- /dev/null
+++ b/tests/topotests/ospfapi/r1/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.4.1/24
diff --git a/tests/topotests/ospfapi/r2/ospfd.conf b/tests/topotests/ospfapi/r2/ospfd.conf
new file mode 100644
index 0000000..1bf2a0c
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r2-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r2-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 2.0.0.0
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r2/zebra.conf b/tests/topotests/ospfapi/r2/zebra.conf
new file mode 100644
index 0000000..e66d52d
--- /dev/null
+++ b/tests/topotests/ospfapi/r2/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r2-eth0
+ ip address 10.0.1.2/24
+!
+interface r2-eth1
+ ip address 10.0.2.2/24
+!
diff --git a/tests/topotests/ospfapi/r3/ospfd.conf b/tests/topotests/ospfapi/r3/ospfd.conf
new file mode 100644
index 0000000..ecf2042
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r3-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r3-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 3.0.0.0
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r3/zebra.conf b/tests/topotests/ospfapi/r3/zebra.conf
new file mode 100644
index 0000000..072c297
--- /dev/null
+++ b/tests/topotests/ospfapi/r3/zebra.conf
@@ -0,0 +1,7 @@
+!
+interface r3-eth0
+ ip address 10.0.2.3/24
+!
+interface r3-eth1
+ ip address 10.0.3.3/24
+!
diff --git a/tests/topotests/ospfapi/r4/ospfd.conf b/tests/topotests/ospfapi/r4/ospfd.conf
new file mode 100644
index 0000000..4de8cae
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/ospfd.conf
@@ -0,0 +1,15 @@
+!
+interface r4-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+interface r4-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf area 1.2.3.4
+!
+router ospf
+ ospf router-id 4.0.0.0
+ capability opaque
+!
diff --git a/tests/topotests/ospfapi/r4/zebra.conf b/tests/topotests/ospfapi/r4/zebra.conf
new file mode 100644
index 0000000..233ffa5
--- /dev/null
+++ b/tests/topotests/ospfapi/r4/zebra.conf
@@ -0,0 +1,6 @@
+!
+interface r4-eth0
+ ip address 10.0.3.4/24
+!
+interface r4-eth1
+ ip address 10.0.4.4/24 \ No newline at end of file
diff --git a/tests/topotests/ospfapi/test_ospf_clientapi.py b/tests/topotests/ospfapi/test_ospf_clientapi.py
new file mode 100644
index 0000000..a7179c5
--- /dev/null
+++ b/tests/topotests/ospfapi/test_ospf_clientapi.py
@@ -0,0 +1,1147 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 eval: (blacken-mode 1) -*-
+#
+# Copyright (c) 2021-2022, LabN Consulting, L.L.C.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; see the file COPYING; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+
+"""
+test_ospf_clientapi.py: Test the OSPF client API.
+"""
+
+import logging
+import os
+import re
+import signal
+import subprocess
+import sys
+import time
+from datetime import datetime, timedelta
+
+import pytest
+
+from lib.common_config import (
+ retry,
+ run_frr_cmd,
+ step,
+ kill_router_daemons,
+ start_router_daemons,
+ shutdown_bringup_interface,
+)
+
+from lib.micronet import Timeout, comm_error
+from lib.topogen import Topogen, TopoRouter
+from lib.topotest import interface_set_status, json_cmp
+
+pytestmark = [pytest.mark.ospfd]
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+TESTDIR = os.path.abspath(CWD)
+
+CLIENTDIR = os.path.abspath(os.path.join(CWD, "../../../ospfclient"))
+if not os.path.exists(CLIENTDIR):
+ CLIENTDIR = os.path.join(CWD, "/usr/lib/frr")
+
+assert os.path.exists(
+ os.path.join(CLIENTDIR, "ospfclient.py")
+), "can't locate ospfclient.py"
+
+
+# ----------
+# Test Setup
+# ----------
+
+#
+# r1 - r2
+# | |
+# r4 - r3
+#
+
+
+@pytest.fixture(scope="function", name="tgen")
+def _tgen(request):
+ "Setup/Teardown the environment and provide tgen argument to tests"
+ nrouters = request.param
+ topodef = {f"sw{i}": (f"r{i}", f"r{i+1}") for i in range(1, nrouters)}
+ if nrouters == 4:
+ topodef["sw4"] = ("r4", "r1")
+
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for _, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_OSPF, "ospfd.conf")
+ router.net.daemons_options["ospfd"] = "--apiserver"
+
+ tgen.start_router()
+
+ yield tgen
+
+ tgen.stop_topology()
+
+
+# Fixture that executes before each test
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+# ------------
+# Test Utility
+# ------------
+
+
+@retry(retry_timeout=45)
+def verify_ospf_database(tgen, dut, input_dict, cmd="show ip ospf database json"):
+ del tgen
+ show_ospf_json = run_frr_cmd(dut, cmd, isjson=True)
+ if not bool(show_ospf_json):
+ return "ospf is not running"
+ result = json_cmp(show_ospf_json, input_dict)
+ return str(result) if result else None
+
+
+def myreadline(f):
+ buf = b""
+ while True:
+ # logging.debug("READING 1 CHAR")
+ c = f.read(1)
+ if not c:
+ return buf if buf else None
+ buf += c
+ # logging.debug("READ CHAR: '%s'", c)
+ if c == b"\n":
+ return buf
+
+
+def _wait_output(p, regex, maxwait=120):
+ timeout = Timeout(maxwait)
+ while not timeout.is_expired():
+ # line = p.stdout.readline()
+ line = myreadline(p.stdout)
+ if not line:
+ assert None, "EOF waiting for '{}'".format(regex)
+ line = line.decode("utf-8")
+ line = line.rstrip()
+ if line:
+ logging.debug("GOT LINE: '%s'", line)
+ m = re.search(regex, line)
+ if m:
+ return m
+ assert None, "Failed to get output matching '{}' withint {} actual {}s".format(
+ regex, maxwait, timeout.elapsed()
+ )
+
+
+# -----
+# Tests
+# -----
+
+
+def _test_reachability(tgen, testbin):
+ waitlist = [
+ "1.0.0.0,2.0.0.0,4.0.0.0",
+ "2.0.0.0,4.0.0.0",
+ "1.0.0.0,2.0.0.0,4.0.0.0",
+ ]
+ r2 = tgen.gears["r2"]
+ r3 = tgen.gears["r3"]
+ r4 = tgen.gears["r4"]
+
+ wait_args = [f"--wait={x}" for x in waitlist]
+
+ p = None
+ try:
+ step("reachable: check for initial reachability")
+ p = r3.popen(
+ ["/usr/bin/timeout", "120", testbin, "-v", *wait_args],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
+
+ step("reachable: check for modified reachability")
+ interface_set_status(r2, "r2-eth0", False)
+ interface_set_status(r4, "r4-eth1", False)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
+
+ step("reachable: check for restored reachability")
+ interface_set_status(r2, "r2-eth0", True)
+ interface_set_status(r4, "r4-eth1", True)
+ _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
+ except Exception as error:
+ logging.error("ERROR: %s", error)
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [4], indirect=True)
+def test_ospf_reachability(tgen):
+ testbin = os.path.join(TESTDIR, "ctester.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([testbin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
+ _test_reachability(tgen, testbin)
+
+
+def _test_router_id(tgen, testbin):
+ r1 = tgen.gears["r1"]
+ waitlist = [
+ "1.0.0.0",
+ "1.1.1.1",
+ "1.0.0.0",
+ ]
+
+ mon_args = [f"--monitor={x}" for x in waitlist]
+
+ p = None
+ try:
+ step("router id: check for initial router id")
+ p = r1.popen(
+ ["/usr/bin/timeout", "120", testbin, "-v", *mon_args],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ _wait_output(p, "SUCCESS: {}".format(waitlist[0]))
+
+ step("router id: check for modified router id")
+ r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.1.1.1")
+ _wait_output(p, "SUCCESS: {}".format(waitlist[1]))
+
+ step("router id: check for restored router id")
+ r1.vtysh_multicmd("conf t\nrouter ospf\nospf router-id 1.0.0.0")
+ _wait_output(p, "SUCCESS: {}".format(waitlist[2]))
+ except Exception as error:
+ logging.error("ERROR: %s", error)
+ raise
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_router_id(tgen):
+ testbin = os.path.join(TESTDIR, "ctester.py")
+ rc, o, e = tgen.gears["r1"].net.cmd_status([testbin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", testbin, rc, o, e)
+ _test_router_id(tgen, testbin)
+
+
+def _test_add_data(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+
+ step("add opaque: add opaque link local")
+
+ p = None
+ try:
+ p = r1.popen([apibin, "-v", "add,9,10.0.1.1,230,2,00000202"])
+ input_dict = {
+ "routerId": "1.0.0.0",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ }
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00000202",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-link json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque area, verify link local flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+ p = r1.popen([apibin, "-v", "add,10,1.2.3.4,231,1,00010101"])
+ input_dict = {
+ "routerId": "1.0.0.0",
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ }
+ ],
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ },
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "00010101",
+ },
+ ],
+ }
+ },
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-area json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("reset client, add opaque AS, verify area flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ p = r1.popen([apibin, "-v", "add,11,232,3,deadbeaf01234567"])
+ input_dict = {
+ "routerId": "1.0.0.0",
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ },
+ ],
+ }
+ # Wait for it to show up
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "opaqueData": "deadbeaf01234567",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ step("stop client, verify AS flushing")
+
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ input_dict = {
+ "routerId": "1.0.0.0",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "lsaAge": 3600,
+ },
+ ],
+ }
+ # Wait for it to be flushed
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ step("start client adding opaque domain, verify new sequence number and data")
+
+ # Originate it again
+ p = r1.popen([apibin, "-v", "add,11,232,3,ebadf00d"])
+ input_dict = {
+ "routerId": "1.0.0.0",
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.3",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000002",
+ },
+ ],
+ }
+ assert verify_ospf_database(tgen, r1, input_dict) is None
+
+ input_dict = {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.3",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000002",
+ "opaqueData": "ebadf00d",
+ },
+ ]
+ }
+ # verify content
+ json_cmd = "show ip ospf da opaque-as json"
+ assert verify_ospf_database(tgen, r1, input_dict, json_cmd) is None
+
+ logging.debug("sending interrupt to writer api client")
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if p:
+ logging.debug("cleanup: sending interrupt to writer api client")
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_add_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_add_data(tgen, apibin)
+
+
+def _test_opaque_add_del(tgen, apibin):
+ "Test adding opaque data to domain"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ p = None
+ pread = None
+ # Log to our stdin, stderr
+ pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
+ try:
+ step("reachable: check for add notification")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,9,10.0.1.1,230,1",
+ "add,9,10.0.1.1,230,2,00000202",
+ "wait,1",
+ "add,10,1.2.3.4,231,1",
+ "add,10,1.2.3.4,231,2,0102030405060708",
+ "wait,1",
+ "add,11,232,1",
+ "add,11,232,2,ebadf00d",
+ "wait,20",
+ "del,9,10.0.1.1,230,2,0",
+ "del,10,1.2.3.4,231,2,1",
+ "del,11,232,1,1",
+ ]
+ )
+ add_input_dict = {
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "76bf",
+ },
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "8aa2",
+ },
+ ],
+ "linkLocalOpaqueLsaCount": 2,
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "5bd8",
+ },
+ {
+ "lsId": "231.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "7690",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 2,
+ },
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "5ed5",
+ },
+ {
+ "lsId": "232.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "d9bd",
+ },
+ ],
+ "asExternalOpaqueLsaCount": 2,
+ }
+
+ step("reachable: check for add LSAs")
+ json_cmd = "show ip ospf da json"
+ assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
+
+ numcs = 3
+ json_cmds = [
+ "show ip ospf da opaque-link json",
+ "show ip ospf da opaque-area json",
+ "show ip ospf da opaque-as json",
+ ]
+ add_detail_input_dict = [
+ {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "76bf",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "8aa2",
+ "length": 24,
+ "opaqueId": 2,
+ "opaqueDataLength": 4,
+ },
+ ]
+ }
+ }
+ },
+ {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "5bd8",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "231.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "7690",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ },
+ {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "5ed5",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "232.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "d9bd",
+ "length": 24,
+ "opaqueDataLength": 4,
+ },
+ ],
+ },
+ ]
+ i = 0
+ while i < numcs:
+ step("reachable: check for add LSA details: %s" % json_cmds[i])
+ assert (
+ verify_ospf_database(tgen, r1, add_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ assert (
+ verify_ospf_database(tgen, r2, add_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ i += 1
+
+ # Wait for add notification
+ # RECV: LSA update msg for LSA 232.0.0.3 in area 0.0.0.0 seq 0x80000001 len 24 age 9
+
+ ls_ids = [
+ "230.0.0.1",
+ "230.0.0.2",
+ "231.0.0.1",
+ "231.0.0.2",
+ "232.0.0.1",
+ "232.0.0.2",
+ ]
+ for ls_id in ls_ids:
+ step("reachable: check for API add notification: %s" % ls_id)
+ waitfor = "RECV:.*update msg.*LSA {}.*age ([0-9]+)".format(ls_id)
+ _ = _wait_output(pread, waitfor)
+
+ del_input_dict = {
+ "areas": {
+ "1.2.3.4": {
+ "linkLocalOpaqueLsa": [
+ {
+ "lsId": "230.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "76bf",
+ },
+ {
+ "lsId": "230.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "sequenceNumber": "80000001",
+ "checksum": "8aa2",
+ },
+ ],
+ "linkLocalOpaqueLsaCount": 2,
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "5bd8",
+ },
+ {
+ "lsId": "231.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "sequenceNumber": "80000002",
+ "checksum": "4fe2",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 2,
+ },
+ },
+ "asExternalOpaqueLsa": [
+ {
+ "lsId": "232.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "sequenceNumber": "80000001",
+ "checksum": "5ed5",
+ },
+ {
+ "lsId": "232.0.0.2",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000001",
+ "checksum": "d9bd",
+ },
+ ],
+ "asExternalOpaqueLsaCount": 2,
+ }
+
+ step("reachable: check for explicit withdrawal LSAs")
+ json_cmd = "show ip ospf da json"
+ assert verify_ospf_database(tgen, r1, del_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, del_input_dict, json_cmd) is None
+
+ del_detail_input_dict = [
+ {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "76bf",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "8aa2",
+ "length": 24,
+ "opaqueId": 2,
+ "opaqueDataLength": 4,
+ },
+ ]
+ }
+ }
+ },
+ {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "5bd8",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "lsaAge": 3600,
+ "linkStateId": "231.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000002",
+ "checksum": "4fe2",
+ # data removed
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ ],
+ },
+ },
+ },
+ {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "5ed5",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "232.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "d9bd",
+ "length": 24,
+ "opaqueDataLength": 4,
+ },
+ ],
+ },
+ ]
+ i = 0
+ while i < numcs:
+ step("reachable: check for delete LSA details: %s" % json_cmds[i])
+ assert (
+ verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ assert (
+ verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ i += 1
+
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+
+ del_detail_input_dict = [
+ {
+ "linkLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "230.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "76bf",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "230.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "8aa2",
+ "length": 24,
+ "opaqueId": 2,
+ "opaqueDataLength": 4,
+ },
+ ]
+ }
+ }
+ },
+ {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "lsaAge": 3600,
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000001",
+ "checksum": "5bd8",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "lsaAge": 3600,
+ "linkStateId": "231.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000002",
+ "checksum": "4fe2",
+ # data removed
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ ],
+ },
+ },
+ },
+ {
+ "asExternalOpaqueLsa": [
+ {
+ "linkStateId": "232.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "5ed5",
+ "length": 20,
+ "opaqueDataLength": 0,
+ },
+ {
+ "linkStateId": "232.0.0.2",
+ "advertisingRouter": "1.0.0.0",
+ "lsaAge": 3600,
+ "lsaSeqNumber": "80000001",
+ "checksum": "d9bd",
+ "length": 24,
+ "opaqueDataLength": 4,
+ },
+ ],
+ },
+ ]
+ i = 0
+ while i < numcs:
+ step(
+ "reachable: check for post API shutdown delete LSA details: %s"
+ % json_cmds[i]
+ )
+ assert (
+ verify_ospf_database(tgen, r1, del_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ assert (
+ verify_ospf_database(tgen, r2, del_detail_input_dict[i], json_cmds[i])
+ is None
+ )
+ i += 1
+
+ # step("reachable: check for flush/age out")
+ # # Wait for max age notification
+ # waitfor = "RECV:.*update msg.*LSA {}.*age 3600".format(ls_id)
+ # _wait_output(pread, waitfor)
+ ls_ids = [
+ "230.0.0.2",
+ "231.0.0.2",
+ "232.0.0.1",
+ "230.0.0.1",
+ "231.0.0.1",
+ "232.0.0.2",
+ ]
+ for ls_id in ls_ids:
+ step("reachable: check for API delete notification: %s" % ls_id)
+ waitfor = "RECV:.*delete msg.*LSA {}.*age".format(ls_id)
+ _ = _wait_output(pread, waitfor)
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_delete_data3(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_add_del(tgen, apibin)
+
+
+def _test_opaque_add_restart_add(tgen, apibin):
+ "Test adding an opaque LSA and then restarting ospfd"
+
+ r1 = tgen.gears["r1"]
+ r2 = tgen.gears["r2"]
+
+ p = None
+ pread = None
+ # Log to our stdin, stderr
+ pout = open(os.path.join(r1.net.logdir, "r1/add-del.log"), "a+")
+ try:
+ step("reachable: check for add notification")
+ pread = r2.popen(
+ ["/usr/bin/timeout", "120", apibin, "-v", "--logtag=READER", "wait,120"],
+ encoding=None, # don't buffer
+ stdin=subprocess.DEVNULL,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,10,1.2.3.4,231,1",
+ "add,10,1.2.3.4,231,1,feedaceebeef",
+ "wait, 5",
+ "add,10,1.2.3.4,231,1,feedaceedeadbeef",
+ "wait, 5",
+ "add,10,1.2.3.4,231,1,feedaceebaddbeef",
+ "wait, 5",
+ ]
+ )
+ add_input_dict = {
+ "areas": {
+ "1.2.3.4": {
+ "areaLocalOpaqueLsa": [
+ {
+ "lsId": "231.0.0.1",
+ "advertisedRouter": "1.0.0.0",
+ "sequenceNumber": "80000004",
+ "checksum": "3128",
+ },
+ ],
+ "areaLocalOpaqueLsaCount": 1,
+ },
+ },
+ }
+ step("Check for add LSAs")
+ json_cmd = "show ip ospf da json"
+ assert verify_ospf_database(tgen, r1, add_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_input_dict, json_cmd) is None
+
+ step("Shutdown the interface on r1 to isolate it for r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
+
+ time.sleep(2)
+ step("Reset the client")
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ step("Kill ospfd on R1")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+ time.sleep(2)
+
+ step("Bring ospfd on R1 back up")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ p = r1.popen(
+ [
+ apibin,
+ "-v",
+ "add,10,1.2.3.4,231,1",
+ "add,10,1.2.3.4,231,1,feedaceecafebeef",
+ "wait, 5",
+ ]
+ )
+
+ step("Bring the interface on r1 back up for connection to r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
+
+ step("Verify area opaque LSA refresh")
+ json_cmd = "show ip ospf da opaque-area json"
+ add_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000005",
+ "checksum": "a87e",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ }
+ assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
+
+ step("Shutdown the interface on r1 to isolate it for r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", False)
+
+ time.sleep(2)
+ step("Reset the client")
+ p.send_signal(signal.SIGINT)
+ time.sleep(2)
+ p.wait()
+ p = None
+
+ step("Kill ospfd on R1")
+ kill_router_daemons(tgen, "r1", ["ospfd"])
+ time.sleep(2)
+
+ step("Bring ospfd on R1 back up")
+ start_router_daemons(tgen, "r1", ["ospfd"])
+
+ step("Bring the interface on r1 back up for connection to r2")
+ shutdown_bringup_interface(tgen, "r1", "r1-eth0", True)
+
+ step("Verify area opaque LSA Purging")
+ json_cmd = "show ip ospf da opaque-area json"
+ add_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [
+ {
+ "lsaAge": 3600,
+ "linkStateId": "231.0.0.1",
+ "advertisingRouter": "1.0.0.0",
+ "lsaSeqNumber": "80000005",
+ "checksum": "a87e",
+ "length": 28,
+ "opaqueDataLength": 8,
+ },
+ ],
+ },
+ },
+ }
+ assert verify_ospf_database(tgen, r1, add_detail_input_dict, json_cmd) is None
+ assert verify_ospf_database(tgen, r2, add_detail_input_dict, json_cmd) is None
+ step("Verify Area Opaque LSA removal after timeout (60 seconds)")
+ time.sleep(60)
+ json_cmd = "show ip ospf da opaque-area json"
+ timeout_detail_input_dict = {
+ "areaLocalOpaqueLsa": {
+ "areas": {
+ "1.2.3.4": [],
+ },
+ },
+ }
+ assert (
+ verify_ospf_database(tgen, r1, timeout_detail_input_dict, json_cmd) is None
+ )
+ assert (
+ verify_ospf_database(tgen, r2, timeout_detail_input_dict, json_cmd) is None
+ )
+
+ except Exception:
+ if p:
+ p.terminate()
+ if p.wait():
+ comm_error(p)
+ p = None
+ raise
+ finally:
+ if pread:
+ pread.terminate()
+ pread.wait()
+ if p:
+ p.terminate()
+ p.wait()
+
+
+@pytest.mark.parametrize("tgen", [2], indirect=True)
+def test_ospf_opaque_restart(tgen):
+ apibin = os.path.join(CLIENTDIR, "ospfclient.py")
+ rc, o, e = tgen.gears["r2"].net.cmd_status([apibin, "--help"])
+ logging.debug("%s --help: rc: %s stdout: '%s' stderr: '%s'", apibin, rc, o, e)
+ _test_opaque_add_restart_add(tgen, apibin)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json
new file mode 100644
index 0000000..74a0de4
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_topo1.json
@@ -0,0 +1,198 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r0-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR0"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json
new file mode 100644
index 0000000..27b36ae
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_asbr_summary_type7_lsa.json
@@ -0,0 +1,202 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json
new file mode 100644
index 0000000..08ff253
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_authentication.json
@@ -0,0 +1,169 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ },
+ "redistribute": [
+ {
+ "redist_type": "static"
+ },
+ {
+ "redist_type": "connected"
+ }
+ ]
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json
new file mode 100644
index 0000000..5555d92
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_dual_stack.json
@@ -0,0 +1,312 @@
+{
+ "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": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ },
+ "ospf": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.0"
+ },
+ "ospf": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.0.4.17",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ },
+ "ospf": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
new file mode 100644
index 0000000..22f46e2
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp.json
@@ -0,0 +1,377 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link4": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link5": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link6": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link7": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r1-link1": {
+ "nbr": "r1"
+ },
+ "r1-link2": {
+ "nbr": "r1"
+ },
+ "r1-link3": {
+ "nbr": "r1"
+ },
+ "r1-link4": {
+ "nbr": "r1"
+ },
+ "r1-link5": {
+ "nbr": "r1"
+ },
+ "r1-link6": {
+ "nbr": "r1"
+ },
+ "r1-link7": {
+ "nbr": "r1"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link4": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link5": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link6": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r0-link7": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r0-link1": {
+ "nbr": "r0"
+ },
+ "r0-link2": {
+ "nbr": "r0"
+ },
+ "r0-link3": {
+ "nbr": "r0"
+ },
+ "r0-link4": {
+ "nbr": "r0"
+ },
+ "r0-link5": {
+ "nbr": "r0"
+ },
+ "r0-link6": {
+ "nbr": "r0"
+ },
+ "r0-link7": {
+ "nbr": "r0"
+ },
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json
new file mode 100644
index 0000000..53b3f49
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_ecmp_lan.json
@@ -0,0 +1,264 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r4": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r5": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r6": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r7": {
+
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {},
+ "r4": {},
+ "r5": {},
+ "r6": {},
+ "r7": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {},
+ "r4": {},
+ "r5": {},
+ "r6": {},
+ "r7": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+
+ "ipv6": "auto",
+ "description": "DummyIntftoR1"
+
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r4": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.4",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r5": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.5",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r6": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.6",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ },
+ "r7": {
+ "links": {
+ "lo": {
+
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.7",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json
new file mode 100644
index 0000000..3a2fc02
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_lan.json
@@ -0,0 +1,140 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "switches": {
+ "s1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 98
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 99
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.3",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "priority": 0
+ }
+ }
+ }
+ }
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1"
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa.json
new file mode 100644
index 0000000..2b91abc
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa.json
@@ -0,0 +1,86 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "lo_prefix": {
+ "ipv6": "2001::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv6": "12::1/64",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.1.1.1",
+ "neighbors": {
+ "r2": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "12::2/64",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "23::2/64",
+ "ospf6": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "2.2.2.2",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv6": "23::3/64",
+ "ospf6": {
+ "area": "1.1.1.1",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "3.3.3.3",
+ "neighbors": {
+ "r2": {}
+ }
+ }
+ }
+ }
+}
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json
new file mode 100644
index 0000000..b1432b9
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_nssa2.json
@@ -0,0 +1,197 @@
+
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r1": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.2",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.3"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "area": [
+ {
+ "id": "0.0.0.2",
+ "type": "nssa"
+ }
+ ],
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
new file mode 100644
index 0000000..a1c7bd7
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_routemaps.json
@@ -0,0 +1,180 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json
new file mode 100644
index 0000000..e70481a
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_rte_calc.json
@@ -0,0 +1,190 @@
+{
+ "feature": [
+ "bgp"
+ ],
+ "address_types": [
+ "ipv6"
+ ],
+
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto"
+ },
+ "r1": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.3",
+ "neighbors": {
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json b/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json
new file mode 100644
index 0000000..d93eb1f
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/ospfv3_single_area.json
@@ -0,0 +1,190 @@
+{
+ "address_types": [
+ "ipv6"
+ ],
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r0": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.0",
+ "neighbors": {
+ "r1": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR3"
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.1",
+ "neighbors": {
+ "r0": {},
+ "r2": {},
+ "r3": {}
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r3": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "100.1.1.2",
+ "neighbors": {
+ "r1": {},
+ "r0": {},
+ "r3": {}
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r0": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4,
+ "network": "point-to-point"
+ }
+ },
+ "r1": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r2": {
+ "ipv6": "auto",
+ "ospf6": {
+ "area": "0.0.0.0",
+ "hello_interval": 1,
+ "dead_interval": 4
+ }
+ },
+ "r1-link0": {
+ "ipv6": "auto",
+ "description": "DummyIntftoR1",
+ "ospf6": {
+ "area": "0.0.0.0"
+ }
+ }
+ },
+ "ospf6": {
+ "router_id": "1.0.4.17",
+ "neighbors": {
+ "r0": {},
+ "r1": {},
+ "r2": {}
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
new file mode 100644
index 0000000..59ba823
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_asbr_summary_topo1.py
@@ -0,0 +1,2753 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Summarisation Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+from time import sleep
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ kill_router_daemons,
+ write_test_footer,
+ reset_config_on_routers,
+ stop_router,
+ start_router,
+ verify_rib,
+ create_static_routes,
+ step,
+ start_router_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_prefix_lists,
+ create_route_maps,
+ topo_daemons,
+ create_interfaces_cfg,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf_summary,
+)
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": [
+ "2011:0:20::1/128",
+ "2011:0:20::2/128",
+ "2011:0:20::3/128",
+ "2011:0:20::4/128",
+ "2011:0:20::5/128",
+ ],
+}
+NETWORK_11 = {
+ "ipv4": ["11.0.20.6/32", "11.0.20.7/32"],
+ "ipv6": ["2011:0:20::6/128", "2011:0:20::7/128"],
+}
+
+NETWORK2 = {
+ "ipv4": [
+ "12.0.20.1/32",
+ "12.0.20.2/32",
+ "12.0.20.3/32",
+ "12.0.20.4/32",
+ "12.0.20.5/32",
+ ],
+ "ipv6": [
+ "2012:0:20::1/128",
+ "2012:0:20::2/128",
+ "2012:0:20::3/128",
+ "2012:0:20::4/128",
+ "2012:0:20::5/128",
+ ],
+}
+SUMMARY = {
+ "ipv4": ["11.0.0.0/8", "12.0.0.0/8", "11.0.0.0/24"],
+ "ipv6": ["2011::/32", "2012::/32", "2011::/64", "2011::/24"],
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A0 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A0
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A0 +---+
+
+TESTCASES =
+1. OSPF summarisation functionality.
+2. OSPF summarisation with advertise and no advertise option
+3. OSPF summarisation with route map modification of metric type.
+4. OSPF CLI Show verify ospf ASBR summary config and show commands behaviours.
+5. OSPF summarisation Chaos.
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_asbr_summary_topo1.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def red_static(dut, config=True):
+ """
+ Local 'def' for Redstribute static routes inside ospf.
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """
+ Local 'def' for Redstribute connected routes inside ospf
+
+ Parameters
+ ----------
+ * `dut` : DUT on which configs have to be made.
+ * `config` : True or False, True by default for configure, set False for
+ unconfiguration.
+ """
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "delete": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_type5_summary_tc42_p0(request):
+ """OSPF summarisation functionality."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route. with aggregate timer as 6 sec"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ],
+ "aggr_timer": 6,
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "del_aggr_timer": True,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ dut = "r1"
+ step("All 5 routes are advertised after deletion of configured summary.")
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("configure the summary again and delete static routes .")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ step("Verify that summary route is withdrawn from R1.")
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Add back static routes.")
+ input_dict_static_rtes = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1."
+ )
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show configure summaries.")
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure new static route which is matching configured summary.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [{"network": NETWORK_11["ipv6"], "next_hop": "blackhole"}]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete one of the static route.")
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv6"], "next_hop": "blackhole", "delete": True}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted static route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step(
+ "Configure redistribute connected and configure ospf external"
+ " summary address to summarise the connected routes."
+ )
+
+ dut = "r0"
+ red_connected(dut)
+ clear_ospf(tgen, dut, ospf="ospf6")
+
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [{"prefix": ip_net.split("/")[0], "mask": "8"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured "
+ "summary address on R0 and only one route is sent to R1."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "fd00::/64"}]}}
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Shut one of the interface")
+ intf = topo["routers"]["r0"]["links"]["r3-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Un do shut the interface")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ # step("Verify that deleted connected route is removed from ospf LSDB.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete OSPF process.")
+ ospf_del = {"r0": {"ospf6": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("Reconfigure ospf process with summary")
+ reset_config_on_routers(tgen)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+ red_connected(dut)
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 and only one route is sent to R1."
+ )
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # step("verify that summary lsa is not refreshed.")
+ # show ip ospf database command is not working, waiting for DEV fix.
+
+ step("Delete the redistribute command in ospf.")
+ dut = "r0"
+ red_connected(dut, config=False)
+ red_static(dut, config=False)
+
+ step("Verify that summary route is withdrawn from the peer.")
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "metric": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc43_p0(request):
+ """OSPF summarisation with metric type 2."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change the summary address mask to lower match (ex - 16 to 8)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "16"},
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+
+ input_dict = {
+ "2011::/16": {
+ "Summary address": "2011::/16",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Change the summary address mask to higher match (ex - 8 to 24)")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "2011::/32": {
+ "Summary address": "2011::/32",
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+ step("Configure 2 summary address with different mask of same network.")
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/32"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(" Un configure one of the summary address.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ sleep(5)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with newly configured mask."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "16"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised "
+ "to configured summary address with highest match."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": "2011::0/16"}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def ospfv3_type5_summary_tc45_p0(request):
+ """OSPF summarisation with Tag option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": "1234",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv6"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(
+ tgen, dut, input_dict_summary, tag="88888", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv6",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Repeat steps 1 to 10 of summarisation in non Back bone area.")
+ reset_config_on_routers(tgen)
+
+ step("Change the area id on the interface on R0")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the area id on the interface ")
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": "1234",
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and only one route is sent to R1 with configured tag."
+ )
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "1234"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Configure Min tag value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32", "tag": 1}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "1"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries with tag.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure Max Tag Value")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": 4294967295,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "4294967295"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 4294967295,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("configure new static route with different tag.")
+ input_dict_static_rtes_11 = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK_11["ipv6"], "next_hop": "blackhole", "tag": "88888"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes_11)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("New tag has not been used by summary address.")
+
+ input_dict_summary = {
+ "r0": {"static_routes": [{"network": SUMMARY["ipv6"][0], "tag": "88888"}]}
+ }
+ dut = "r1"
+
+ result = verify_ospf6_rib(
+ tgen, dut, input_dict_summary, tag="88888", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ "ipv6",
+ dut,
+ input_dict_summary,
+ protocol=protocol,
+ tag="88888",
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Verify that boundary values tags are used for summary route"
+ " using show ip ospf route command."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 88888,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary address")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": 4294967295,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that 6 routes are advertised to neighbour with 5 routes"
+ " without any tag, 1 route with tag."
+ )
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that summary address is flushed from neighbor.")
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Configure summary first & then configure matching static route.")
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole", "delete": True},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole", "delete": True},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc46_p0(request):
+ """OSPF summarisation with advertise and no advertise option"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5"
+ " routes to one route with no advertise option."
+ )
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Delete the configured summary")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Summary has 5 sec delay timer, sleep 5 secs...")
+ sleep(5)
+
+ step("Verify that summary lsa is withdrawn from R1 and deleted from R0.")
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary Route still present in RIB".format(
+ tc_name
+ )
+
+ step("show ip ospf summary should not have any summary address.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 1234,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(
+ tgen, topo, dut, input_dict, ospf="ospf6", expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Summary still present in DB".format(tc_name)
+
+ step("Reconfigure summary with no advertise.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary"
+ " address on R0 and summary route is not advertised to neighbor as"
+ " no advertise is configured.."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the " "configured summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step(
+ "Change summary address from no advertise to advertise "
+ "(summary-address 10.0.0.0 255.255.0.0)"
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "advertise": False,
+ }
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ output = tgen.gears["r0"].vtysh_cmd(
+ "show ipv6 ospf6 database as-external json", isjson=True
+ )
+
+ output = tgen.gears["r1"].vtysh_cmd(
+ "show ipv6 ospf6 database as-external json", isjson=True
+ )
+
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc48_p0(request):
+ """OSPF summarisation with route map modification of metric type."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step(
+ "Configure route map and & rule to permit configured summary address,"
+ " redistribute static & connected routes with the route map."
+ )
+ step("Configure prefixlist to permit the static routes, add to route map.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured"
+ "summary address on R0 and only one route is sent to R1. Verify that "
+ "show ip ospf summary should show the configure summaries."
+ )
+
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Configure metric type as 1 in route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "seq_id": "1",
+ "action": "permit",
+ "set": {"metric-type": "type-1"},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Un configure metric type from route map.")
+
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": "1",
+ "set": {"metric-type": "type-1", "delete": True},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes(static / connected) are summarised"
+ " to configured summary address with metric type 2."
+ )
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Change rule from permit to deny in prefix list.")
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "deny"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that previously originated summary lsa "
+ "is withdrawn from the neighbor."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ step("summary route has delay of 5 secs, wait for 5 secs")
+
+ sleep(5)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict_summary, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc51_p2(request):
+ """OSPF CLI Show.
+
+ verify ospf ASBR summary config and show commands behaviours.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure all the supported OSPF ASBR summary commands on DUT.")
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "32",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure and re configure all the commands 10 times in a loop.")
+
+ for itrate in range(0, 10):
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "8",
+ "tag": 4294967295,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "16",
+ "advertise": True,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ {
+ "prefix": SUMMARY["ipv6"][0].split("/")[0],
+ "mask": "24",
+ "advertise": False,
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify the show commands")
+
+ input_dict = {
+ SUMMARY["ipv6"][3]: {
+ "Summary address": SUMMARY["ipv6"][3],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 0,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_type5_summary_tc49_p2(request):
+ """OSPF summarisation Chaos."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ protocol = "ospf"
+
+ step(
+ "Configure 5 static routes from the same network on R0"
+ "5 static routes from different networks and redistribute in R0"
+ )
+ input_dict_static_rtes = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"], "next_hop": "blackhole"},
+ {"network": NETWORK2["ipv6"], "next_hop": "blackhole"},
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that routes are learnt on R1.")
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_static_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ result = verify_rib(tgen, "ipv6", dut, input_dict_static_rtes, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step(
+ "Configure External Route summary in R0 to summarise 5" " routes to one route."
+ )
+
+ ospf_summ_r1 = {
+ "r0": {
+ "ospf6": {
+ "summary-address": [
+ {"prefix": SUMMARY["ipv6"][0].split("/")[0], "mask": "32"}
+ ]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_summ_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r0")
+ start_router(tgen, "r0")
+
+ step(
+ "Verify that external routes are summarised to configured summary "
+ "address on R0 after 5 secs of delay timer expiry and only one "
+ "route is sent to R1."
+ )
+ input_dict_summary = {"r0": {"static_routes": [{"network": SUMMARY["ipv6"][0]}]}}
+ dut = "r1"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict_summary)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict_summary, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Verify that show ip ospf summary should show the summaries.")
+ input_dict = {
+ SUMMARY["ipv6"][0]: {
+ "Summary address": SUMMARY["ipv6"][0],
+ "Metric-type": "E2",
+ "Metric": 20,
+ "Tag": 0,
+ "External route count": 5,
+ }
+ }
+ dut = "r0"
+ result = verify_ospf_summary(tgen, topo, dut, input_dict, ospf="ospf6")
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Summary missing in OSPF DB".format(tc_name)
+
+ step("Verify that originally advertised routes are withdraw from there" " peer.")
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"], "next_hop": "blackhole"}]}
+ }
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Error: " "Routes still present in OSPF RIB {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes still present in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py
new file mode 100644
index 0000000..d32a05a
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_authentication.py
@@ -0,0 +1,1446 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from time import sleep
+from copy import deepcopy
+import json
+from lib.topotest import frr_unicode
+
+pytestmark = pytest.mark.ospf6d
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_topo_from_json, build_config_from_json
+from lib.ospf import verify_ospf6_neighbor, config_ospf6_interface, clear_ospf
+from ipaddress import IPv4Address
+
+# Global variables
+topo = None
+# Reading the data from JSON File for topology creation
+jsonFile = "{}/ospfv3_authentication.json".format(CWD)
+try:
+ with open(jsonFile, "r") as topoJson:
+ topo = json.load(topoJson)
+except IOError:
+ assert False, "Could not read file {}".format(jsonFile)
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 manual key configuration.
+2. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 manual key configuration.
+3. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 keychain configuration.
+4. OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 keychain configuration.
+
+ """
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_single_area.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf6_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf6_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+def test_ospf6_auth_trailer_tc1_md5(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 manual key configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Disable authentication on R2 "
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ "del_action": True
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=5
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Again On R2 enable ospf6 on interface with message-digest auth")
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospf6_auth_trailer_tc2_sha256(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 manual key configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Disable authentication on R2 "
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ "del_action": True
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=5
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Again On R2 enable ospf6 on interface with message-digest auth")
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc3_keychain_md5(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 keychain configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm md5"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm md5"""
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Disable authentication on R2 "
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ "del_action": True
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=5
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Again On R2 enable ospf6 on interface with message-digest auth")
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc4_keychain_sha256(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 keychain configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Disable authentication on R2 "
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ "del_action": True
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify on R1 ,nbr is deleted for R2 after dead interval expiry")
+ # wait till the dead timer expiry
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=5
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Again On R2 enable ospf6 on interface with message-digest auth")
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using"
+ " show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step("Shut no shut interface on R1")
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ dut = "r2"
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, expected=False)
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ dut = "r1"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 using "
+ "show ip ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc5_md5_keymissmatch(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 manual key configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer wrong key"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6-missmatch",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is not FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ step("Verify that the neighbour is FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer correct key"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "md5",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc6_sha256_mismatch(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 manual key configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 with on R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer wrong key"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6-missmatch",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 with on R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer wrong key"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "hash-algo": "hmac-sha-256",
+ "key": "ospf6",
+ "key-id": "10",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc7_keychain_md5_missmatch(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using MD5 keychain configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm md5"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm md5"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth-missmatch
+ key 10
+ key-string ospf6-missmatch
+ cryptographic-algorithm md5"""
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer with wrong keychain"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth-missmatch",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer with correct keychain"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc8_keychain_sha256_missmatch(request):
+ """
+ OSPFv3 Authentication Trailer - Verify ospfv3 authentication trailer
+ using HMAC-SHA-256 keychain configuration.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ router1.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth
+ key 10
+ key-string ospf6
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ router2.vtysh_cmd(
+ """configure terminal
+ key chain auth-missmatch
+ key 10
+ key-string ospf6-missmatch
+ cryptographic-algorithm hmac-sha-256"""
+ )
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer wrong keychain"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth-missmatch",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer correct keychain"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc9_keychain_not_configured(request):
+ """
+ OSPFv3 Neighborship without Authentication Trailer -
+ Verify ospfv3 neighborship when no authentication trailer is configured.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 auth on R1 interface "
+ "connected to R2 with auth trailer"
+ )
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ r1_ospf6_auth = {
+ "r1": {
+ "links": {
+ "r2": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r1"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ step(
+ "Configure ospf6 between R1 and R2, enable ospf6 on R2 interface "
+ "connected to R1 with auth trailer non existing keychain"
+ )
+
+ r2_ospf6_auth = {
+ "r2": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "keychain": "auth",
+ }
+ }
+ }
+ }
+ }
+ result = config_ospf6_interface(tgen, topo, r2_ospf6_auth)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that the neighbour is not FULL between R1 and R2.")
+ # wait for dead time expiry.
+ sleep(6)
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(
+ tgen, topo, dut=dut, expected=False, retry_timeout=3
+ )
+ assert ospf6_covergence is not True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+def test_ospf6_auth_trailer_tc10_no_auth_trailer(request):
+ """
+ OSPFv3 Neighborship without Authentication Trailer -
+ Verify ospfv3 neighborship when no authentication trailer is configured.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ router1 = tgen.gears["r1"]
+ router2 = tgen.gears["r2"]
+
+ step(
+ "Verify that the neighbour is FULL between R1 and R2 "
+ "using show ipv6 ospf6 neighbor cmd."
+ )
+
+ dut = "r2"
+ ospf6_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf6_covergence is True, "Testcase {} :Failed \n Error:" " {}".format(
+ tc_name, ospf6_covergence
+ )
+
+ write_test_footer(tc_name)
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
new file mode 100644
index 0000000..75be092
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp.py
@@ -0,0 +1,489 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ shutdown_bringup_interface,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ verify_ospf6_rib,
+ create_router_ospf,
+ config_ospf6_interface,
+)
+
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+"""
+TOPOLOGY :
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES :
+1. Verify OSPF ECMP with max path configured as 8 (ECMPconfigured at FRR level)
+2. Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_ecmp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_ecmp_tc16_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8 (ECMP
+ configured at FRR level)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 8 interfaces between R1 and R2 and enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route in R2 in stalled with 8 next hops.")
+ nh = []
+ for item in range(1, 7):
+ nh.append(llip)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh.append(nh2)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut all the interfaces on the remote router - R2")
+ dut = "r1"
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in OSPF RIB. Error: {}".format(
+ tc_name, result
+ )
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("shut no shut on all the interfaces on DUT (r1)")
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ for intfr in range(1, 7):
+ intf = topo["routers"]["r1"]["links"]["r0-link{}".format(intfr)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "Verify that all the neighbours are up and routes are installed"
+ " with 8 next hop in ospf and ip route tables on R1."
+ )
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_ecmp_tc17_p0(request):
+ """
+ Verify OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 2 (Edge having 2 uplink ports)
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure 2 interfaces between R1 and R2 & enable ospf in area 0.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 2 neighborship sessions.")
+ dut = "r1"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Configure a static route in R0 and redistribute in OSPF.")
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ red_static(dut)
+
+ step("Verify that route in R2 in stalled with 2 next hops.")
+
+ llip = get_llip("r0", "r1-link1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh1 = llip
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh2 = llip
+
+ nh = [nh1, nh2]
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure ECMP value as 1.")
+ max_path = {"r1": {"ospf6": {"maximum-paths": 1}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ max_path = {"r1": {"ospf6": {"maximum-paths": 2}}}
+ result = create_router_ospf(tgen, topo, max_path)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure cost on R0 as 100")
+ r0_ospf_cost = {"r0": {"links": {"r1": {"ospf6": {"cost": 100}}}}}
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ 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/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
new file mode 100644
index 0000000..ce880b4
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_ecmp_lan.py
@@ -0,0 +1,400 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import json
+from copy import deepcopy
+from ipaddress import IPv4Address
+from lib.topotest import frr_unicode
+import ipaddress
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ create_router_ospf,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ config_ospf6_interface,
+)
+
+from ipaddress import IPv6Address
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+MASK = {"ipv6": "32", "ipv6": "128"}
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ Topo : Broadcast Networks
+ +---+ +---+ +---+ +---+
+ |R0 + +R1 + +R2 + +R3 |
+ +-+-+ +-+-+ +-+-+ +-+-+
+ | | | |
+ | | | |
+ --+-----------+--------------+---------------+-----
+ Ethernet Segment
+
+TESTCASES =
+1. Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ global topo, switch_name
+ 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...
+ json_file = "{}/ospfv3_ecmp_lan.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ switch_name = [k for k in topo["switches"].keys()][0]
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology")
+
+ tgen = get_topogen()
+
+ # Stop toplogy and Remove tmp files
+
+ try:
+ # Stop toplogy and Remove tmp files
+ tgen.stop_topology()
+
+ except OSError:
+ # OSError exception is raised when mininet tries to stop switch
+ # though switch is stopped once but mininet tries to stop same
+ # switch again, where it ended up with exception
+ pass
+ logger.info(
+ "Testsuite end time: {}".format(time.asctime(time.localtime(time.time())))
+ )
+ logger.info("=" * 40)
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_lan_ecmp_tc18_p0(request):
+ """
+ OSPF ECMP.
+
+ Verify OSPF ECMP with max path configured as 8
+ (Edge having 1 uplink port as broadcast network,
+ connect to 8 TORs - LAN case)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step(". Configure ospf in all the routers on LAN interface.")
+
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF is up with 8 neighborship sessions.")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(
+ "Configure a static route in all the routes and "
+ "redistribute static/connected in OSPF."
+ )
+
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = rtr
+ red_static(dut)
+
+ step(
+ "Verify that route in R0 in stalled with 8 hops. "
+ "Verify ospf route table and ip route table."
+ )
+
+ nh = []
+ for rtr in topo["routers"]:
+ llip = get_llip(rtr, switch_name)
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ nh.append(llip)
+
+ llip = get_llip("r1", switch_name)
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ nh.remove(llip)
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" clear ip ospf interface on DUT(r0)")
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step(
+ "Verify that after clearing the ospf interface all the "
+ "neighbours are up and routes are installed with 8 next hop "
+ "in ospf and ip route tables on R0"
+ )
+
+ dut = "r0"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" clear ip ospf interface on R2")
+ clear_ospf(tgen, "r2")
+
+ dut = "r2"
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut, lan=True)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Delete static/connected cmd in ospf in all the routes one by one.")
+ for rtr in topo["routers"]:
+ input_dict = {
+ rtr: {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ 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/ospfv3_basic_functionality/test_ospfv3_nssa.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py
new file mode 100644
index 0000000..bdc4c13
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa.py
@@ -0,0 +1,162 @@
+#!/usr/bin/python
+
+from lib.topogen import Topogen, get_topogen
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.ospf import create_router_ospf, verify_ospf6_neighbor
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+
+pytestmark = [pytest.mark.ospfd]
+
+
+# Global variables
+topo = None
+
+"""
+TOPOOLOGY
+
+ +---+ 0.0.0.0 +---+ 1.1.1.1 +---+
+ +R1 +------------+R2 |------------+R3 |
+ +-+-+ +--++ +--++
+
+TESTCASES =
+1. OSPF Verify E-bit mismatch between R2 and R3
+2. OSPF Verify N-bit mismatch between R2 and R3
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_nssa.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "setup_module: Failed \n Error:" " {}".format(result)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_bit_mismatch(request):
+ """OSPF verify E-bit and N-bit mismatch."""
+
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ input_dict = {"r3": {"ospf6": {"neighbors": []}}}
+
+ step("Configure r3 as stub router")
+ stub = {"r3": {"ospf6": {"area": [{"id": "1.1.1.1", "type": "stub"}]}}}
+ result = create_router_ospf(tgen, topo, stub)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(tc_name, result)
+ # Verify r3 lost its adjacency with r2 due to E-bit mismatch
+ result = verify_ospf6_neighbor(tgen, topo, dut="r3", input_dict=input_dict)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure r2 as stub router")
+ stub = {"r2": {"ospf6": {"area": [{"id": "1.1.1.1", "type": "stub"}]}}}
+ result = create_router_ospf(tgen, topo, stub)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(tc_name, result)
+ # Verify r3 has an adjacency up with r2 again
+ result = verify_ospf6_neighbor(tgen, topo, dut="r3")
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure r3 as NSSA router")
+ nssa = {"r3": {"ospf6": {"area": [{"id": "1.1.1.1", "type": "nssa"}]}}}
+ result = create_router_ospf(tgen, topo, nssa)
+ # Verify r3 lost its adjacency with r2 due to N-bit mismatch
+ result = verify_ospf6_neighbor(tgen, topo, dut="r3", input_dict=input_dict)
+ assert result is True, "Testcase {}: Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure r2 as NSSA router")
+ nssa = {"r2": {"ospf6": {"area": [{"id": "1.1.1.1", "type": "nssa"}]}}}
+ result = create_router_ospf(tgen, topo, nssa)
+ # Verify r3 has an adjacency up with r2 again
+ result = verify_ospf6_neighbor(tgen, topo, dut="r3")
+ 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/ospfv3_basic_functionality/test_ospfv3_nssa2.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
new file mode 100644
index 0000000..7b41c80
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_nssa2.py
@@ -0,0 +1,482 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+from copy import deepcopy
+import ipaddress
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ config_ospf6_interface,
+ clear_ospf,
+ verify_ospf6_rib,
+ verify_ospf6_interface,
+ verify_ospf6_database,
+ create_router_ospf,
+)
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+ verify_bgp_rib,
+)
+from lib.topolog import logger
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ step,
+ topo_daemons,
+ create_route_maps,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ check_router_status,
+)
+from ipaddress import IPv4Address
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+
+# Save the Current Working Directory to find configuration files.
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+# Global variables
+topo = None
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": [
+ "2011:0:20::1/128",
+ "2011:0:20::2/128",
+ "2011:0:20::3/128",
+ "2011:0:20::4/128",
+ "2011:0:20::5/128",
+ ],
+}
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+
+
+TESTCASES =
+1. OSPF Learning - Verify OSPF can learn different types of LSA and
+ processes them.[Edge learning different types of LSAs]
+2. Verify that ospf non back bone area can be configured as NSSA area
+3. Verify that ospf NSSA area DUT is capable receiving & processing
+ Type7 N2 route.
+"""
+
+
+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...
+ json_file = "{}/ospfv3_nssa2.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ 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()
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_nssa_tc26_p0(request):
+ """Verify that ospf non back bone area can be configured as NSSA area"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure ospf area 2 on r0 , r1 & r4, make the area 2 as NSSA area")
+
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static route in R2 ospf.")
+ dut = "r2"
+ red_static(dut)
+
+ step("Verify that Type 5 LSA is originated by R2.")
+ dut = "r0"
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Un configure redistribute command in R4")
+ dut = "r2"
+ red_static(dut, config=False)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ step("Configure area 0 on interface of r2 connecting to r1")
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbor goes down between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+
+ step("Now configure area 0 on interface of r1 connecting to r2.")
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that ospf neighbour comes up between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure area 2 on interface of r2 connecting to r1.")
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r2": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r2"]["links"]["r1"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbor goes down between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2", expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Nbrs are not down" "Error: {}".format(tc_name, result)
+
+ step("Now configure area 2 on interface of r1 connecting to r2.")
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r1": {
+ "links": {
+ "r2": {
+ "interface": topo["routers"]["r1"]["links"]["r2"]["interface"],
+ "ospf6": {"area": "0.0.0.2"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that ospf neighbour comes up between r2 and r1.")
+ result = verify_ospf6_neighbor(tgen, topo, dut="r2")
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+# As per internal discussion, this script has to be removed as translator
+# function is not supported, for more details kindly check this PR 2565570
+def ospfv3_nssa_tc27_p0(request):
+ """
+ OSPF NSSA.
+
+ Verify that ospf NSSA area DUT is capable receiving & processing
+ Type7 N2 route.
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ step("Configure ospf area 2 on r0 , r1 & r4, make the area 2 as NSSA area")
+
+ reset_config_on_routers(tgen)
+
+ input_dict = {
+ "r2": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute static route in R2 ospf.")
+ dut = "r2"
+ red_static(dut)
+
+ step("Verify that Type 5 LSA is originated by R2.")
+ dut = "r0"
+ protocol = "ospf6"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Un configure redistribute command in R4")
+ dut = "r2"
+ red_static(dut, config=False)
+
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r0"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result is not 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/ospfv3_basic_functionality/test_ospfv3_routemaps.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
new file mode 100644
index 0000000..0c9457b
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_routemaps.py
@@ -0,0 +1,1248 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ create_prefix_lists,
+ verify_rib,
+ create_static_routes,
+ step,
+ create_route_maps,
+ verify_prefix_lists,
+ topo_daemons,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ verify_ospf6_rib,
+ create_router_ospf,
+)
+
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+
+routerids = ["100.1.1.0", "100.1.1.1", "100.1.1.2", "100.1.1.3"]
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+2. Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+4. Verify OSPF route map support functionality
+ when route map actions are toggled.
+5. Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+6. Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+7. Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+8. Verify OSPF route map applied to ospf redistribution with ipv6 prefix list
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_routemaps.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_routemaps_functionality_tc19_p0(request):
+ """
+ OSPF Route map - Verify OSPF route map support functionality.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {"r0": {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ lsid = NETWORK["ipv6"][0].split("/")[0]
+ rid = routerids[0]
+
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {"redistribute": [{"redist_type": "static", "del_action": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create prefix-list in R0 to permit 10.0.20.1/32 prefix &" " deny 10.0.20.2/32"
+ )
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][0],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n, prefix list creation failed. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure route map rmap1 and redistribute static routes to"
+ " ospf using route map rmap1"
+ )
+
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Verify that route map is activated in OSPF.")
+
+ step("Verify that route 10.0.20.1 is allowed and 10.0.20.2 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ lsid = NETWORK["ipv6"][1].split("/")[0]
+ rid = routerids[0]
+
+ step("Change prefix rules to permit 10.0.20.2 and deny 10.0.20.1")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete and reconfigure prefix list.")
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ "delete": True,
+ },
+ {
+ "seqid": 11,
+ "network": "any",
+ "action": "deny",
+ "delete": True,
+ },
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 5, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {
+ "seqid": 10,
+ "network": NETWORK["ipv6"][1],
+ "action": "permit",
+ },
+ {"seqid": 11, "network": "any", "action": "deny"},
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route 10.0.20.2 is allowed and 10.0.20.1 is denied.")
+ dut = "r1"
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][1], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {"network": NETWORK["ipv6"][0], "no_of_ip": 1, "next_hop": "Null0"}
+ ]
+ }
+ }
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc20_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality when route map is not
+ configured at system level but configured in OSPF
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step("Create static routes(10.0.20.1/32 and 10.0.20.2/32) in R0")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Redistribute to ospf using route map ( non existent route map)")
+ ospf_red_r1 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are not allowed in OSPF even tough no "
+ "matching routing map is configured."
+ )
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "configure the route map with the same name that is used "
+ "in the ospf with deny rule."
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "deny"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that now route map is activated & routes are denied in OSPF.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ step("Delete the route map.")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"action": "deny", "delete": True}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that routes are allowed in OSPF even tough "
+ "no matching routing map is configured."
+ )
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc25_p0(request):
+ """
+ OSPF route map support functionality.
+
+ Verify OSPF route map support functionality
+ when route map actions are toggled.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute "
+ "to OSPF using route map."
+ )
+
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with permit rule")
+ # Create route map
+ routemaps = {"r0": {"route_maps": {"rmap_ipv6": [{"action": "permit"}]}}}
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that route is advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ step("Configure route map with deny rule")
+ # Create route map
+ routemaps = {
+ "r0": {"route_maps": {"rmap_ipv6": [{"seq_id": 10, "action": "deny"}]}}
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that route is not advertised to R1.")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc22_p0(request):
+ """
+ OSPF Route map - Multiple sequence numbers.
+
+ Verify OSPF route map support functionality with multiple sequence
+ numbers in a single route-map for different match/set clauses.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure route map with seq number 10 to with ip prefix"
+ " permitting route 10.0.20.1/32 in R1"
+ )
+ step(
+ "Configure route map with seq number 20 to with ip prefix"
+ " permitting route 10.0.20.2/32 in R1"
+ )
+
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": "10",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv6"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": NETWORK["ipv6"][0], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ input_dict_2 = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_2_ipv6": [
+ {"seqid": 10, "network": NETWORK["ipv6"][1], "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure static routes 10.0.20.1/32 and 10.0.20.2 in R1")
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure redistribute static route with route map.")
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 2,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that both routes are learned in R1 and R2")
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change route map with seq number 20 to deny.")
+ # Create route map
+ input_dict_3 = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "deny",
+ "seq_id": "20",
+ "match": {"ipv6": {"prefix_lists": "pf_list_2_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify the route 10.0.20.2/32 is withdrawn and not present "
+ "in the routing table of R0 and R1."
+ )
+
+ input_dict = {
+ "r0": {"static_routes": [{"network": NETWORK["ipv6"][1], "next_hop": "Null0"}]}
+ }
+
+ dut = "r1"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "ospf"
+ result = verify_ospf6_rib(tgen, dut, input_dict, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route found in the RIB, Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc23_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we add/remove route-maps
+ with multiple set clauses and without any match statement.(Set only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ " Create static routes(10.0.20.1/32) in R1 and "
+ "redistribute to OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure route map with set clause (set metric)")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {"action": "permit", "seq_id": 10, "set": {"metric": 123}}
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that configured metric is applied to ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, metric=123)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, metric=123)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("un configure the set clause")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "set": {"metric": 123, "delete": True},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict, metric=20)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, metric=20)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_routemaps_functionality_tc24_p0(request):
+ """
+ OSPF Route map - Multiple set clauses.
+
+ Verify OSPF route map support functionality when we
+ add/remove route-maps with multiple match clauses and without
+ any set statement.(Match only)
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config as per the topology")
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ospf_red_r0 = {
+ "r0": {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "route_map": "rmap_ipv6"}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Create static routes(10.0.20.1/32) in R1 and redistribute to "
+ "OSPF using route map."
+ )
+ # Create Static routes
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][1],
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "tag": 1000,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Create ip prefix list
+ pfx_list = {
+ "r0": {
+ "prefix_lists": {
+ "ipv6": {
+ "pf_list_1_ipv6": [
+ {"seqid": 10, "network": "any", "action": "permit"}
+ ]
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, pfx_list)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that prefix-list is created in R0.")
+ result = verify_prefix_lists(tgen, pfx_list)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Prefix list not " "present. Error: {}".format(
+ tc_name, result
+ )
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [{"action": "permit", "match": {"ipv6": {"tag": "1000"}}}]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with tag in route map")
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"tag": "1000", "delete": True}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that metric falls back to original metric for ospf routes.")
+ dut = "r1"
+ protocol = "ospf"
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the match clause with metric in route map.")
+
+ # Create route map
+ routemaps = {
+ "r0": {
+ "route_maps": {
+ "rmap_ipv6": [
+ {
+ "action": "permit",
+ "match": {"ipv6": {"prefix_lists": "pf_list_1_ipv6"}},
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, routemaps)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol)
+ 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/ospfv3_basic_functionality/test_ospfv3_rte_calc.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
new file mode 100644
index 0000000..df3a024
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_rte_calc.py
@@ -0,0 +1,923 @@
+#!/usr/bin/python
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+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, "../"))
+sys.path.append(os.path.join(CWD, "../lib/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+import ipaddress
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ step,
+ shutdown_bringup_interface,
+ create_interfaces_cfg,
+ topo_daemons,
+ get_frr_ipv6_linklocal,
+ check_router_status,
+ create_static_routes,
+)
+
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+from lib.bgp import create_router_bgp, verify_bgp_convergence
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ clear_ospf,
+ verify_ospf6_rib,
+ verify_ospf_database,
+ create_router_ospf,
+ config_ospf6_interface,
+ verify_ospf6_interface,
+)
+
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+NETWORK = {
+ "ipv6": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ ],
+ "ipv6": ["2::1/128", "2::2/128", "2::3/128", "2::4/128", "2::5/128"],
+}
+TOPOOLOGY = """
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+"""
+
+TESTCASES = """
+1. OSPF Cost - verifying ospf interface cost functionality
+"""
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_rte_calc.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def get_llip(onrouter, intf):
+ """
+ API to get the link local ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `fromnode`: Source node
+ * `tonode` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_llip('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) link local ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ tgen = get_topogen()
+ intf = topo["routers"][onrouter]["links"][intf]["interface"]
+ llip = get_frr_ipv6_linklocal(tgen, onrouter, intf)
+ if llip:
+ logger.info("llip ipv6 address to be set as NH is %s", llip)
+ return llip
+ return None
+
+
+def get_glipv6(onrouter, intf):
+ """
+ API to get the global ipv6 address of a particular interface
+
+ Parameters
+ ----------
+ * `onrouter`: Source node
+ * `intf` : interface for which link local ip needs to be returned.
+
+ Usage
+ -----
+ result = get_glipv6('r1', 'r2-link0')
+
+ Returns
+ -------
+ 1) global ipv6 address from the interface.
+ 2) errormsg - when link local ip not found.
+ """
+ glipv6 = (topo["routers"][onrouter]["links"][intf]["ipv6"]).split("/")[0]
+ if glipv6:
+ logger.info("Global ipv6 address to be set as NH is %s", glipv6)
+ return glipv6
+ return None
+
+
+def red_static(dut, config=True):
+ """Local def for Redstribute static routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "static"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "static", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+
+def red_connected(dut, config=True):
+ """Local def for Redstribute connected routes inside ospf."""
+ global topo
+ tgen = get_topogen()
+ if config:
+ ospf_red = {dut: {"ospf6": {"redistribute": [{"redist_type": "connected"}]}}}
+ else:
+ ospf_red = {
+ dut: {
+ "ospf6": {
+ "redistribute": [{"redist_type": "connected", "del_action": True}]
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase: Failed \n Error: {}".format(result)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_redistribution_tc5_p0(request):
+ """Test OSPF intra area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+
+ nh = llip
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_redistribution_tc6_p0(request):
+ """Test OSPF inter area route calculations."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+
+ global topo
+ step("Bring up the base config.")
+ reset_config_on_routers(tgen)
+
+ step("Verify that OSPF neighbors are FULL.")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step("verify intra area route is calculated for r0-r3 interface ip in R1")
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ llip = get_llip("r0", "r1")
+ assert llip is not None, "Testcase {} : Failed \n Error: {}".format(tc_name, llip)
+ nh = llip
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address on newly configured loopback of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh, expected=False)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(
+ tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Route present in RIB. Error: {}".format(tc_name, result)
+
+ step("Add back the deleted ip address on newly configured interface of R0")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Shut no shut interface on R0")
+ dut = "r0"
+ intf = topo["routers"]["r0"]["links"]["r3"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("un shut the OSPF interface on R0")
+ dut = "r0"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "ospf"
+ result = verify_rib(tgen, "ipv6", dut, input_dict, protocol=protocol, next_hop=nh)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_redistribution_tc8_p1(request):
+ """
+ Test OSPF redistribution of connected routes.
+
+ Verify OSPF redistribution of connected routes when bgp multi hop
+ neighbor is configured using ospf routes
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ step(
+ "Configure loopback interface on all routers, and redistribut"
+ "e connected routes into ospf"
+ )
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step(
+ "verify that connected routes -loopback is found in all routers"
+ "advertised/exchaged via ospf"
+ )
+ for rtr in topo["routers"]:
+ red_static(rtr)
+ red_connected(rtr)
+
+ for node in topo["routers"]:
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": topo["routers"][node]["links"]["lo"]["ipv6"],
+ "no_of_ip": 1,
+ }
+ ]
+ }
+ }
+ for rtr in topo["routers"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure E BGP multi hop using the loopback addresses.")
+ as_num = 100
+ for node in topo["routers"]:
+ as_num += 1
+ topo["routers"][node].update(
+ {
+ "bgp": {
+ "local_as": as_num,
+ "address_family": {"ipv6": {"unicast": {"neighbor": {}}}},
+ }
+ }
+ )
+ for node in topo["routers"]:
+ for rtr in topo["routers"]:
+ if node is not rtr:
+ topo["routers"][node]["bgp"]["address_family"]["ipv6"]["unicast"][
+ "neighbor"
+ ].update(
+ {
+ rtr: {
+ "dest_link": {
+ "lo": {"source_link": "lo", "ebgp_multihop": 2}
+ }
+ }
+ }
+ )
+
+ result = create_router_bgp(tgen, topo, topo["routers"])
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ # Modify router id
+ input_dict = {
+ "r0": {"bgp": {"router_id": "11.11.11.11"}},
+ "r1": {"bgp": {"router_id": "22.22.22.22"}},
+ "r2": {"bgp": {"router_id": "33.33.33.33"}},
+ "r3": {"bgp": {"router_id": "44.44.44.44"}},
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that BGP neighbor is ESTABLISHED")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step(
+ "Configure couple of static routes in R0 and "
+ "Redistribute static routes in R1 bgp."
+ )
+
+ for rtr in topo["routers"]:
+ ospf_red = {
+ rtr: {
+ "ospf6": {"redistribute": [{"redist_type": "static", "delete": True}]}
+ }
+ }
+ result = create_router_ospf(tgen, topo, ospf_red)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": NETWORK["ipv6"][0],
+ "no_of_ip": 5,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ configure_bgp_on_r0 = {
+ "r0": {
+ "bgp": {
+ "address_family": {
+ "ipv6": {"unicast": {"redistribute": [{"redist_type": "static"}]}}
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, configure_bgp_on_r0)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Clear ospf neighbours in R0")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr)
+
+ step("Verify that OSPF neighbours are reset and forms new adjacencies.")
+ # Api call verify whether OSPF is converged
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("Verify that BGP neighbours are reset and forms new adjacencies.")
+ result = verify_bgp_convergence(tgen, topo)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ protocol = "bgp"
+ for rtr in ["r1", "r2", "r3"]:
+ result = verify_rib(tgen, "ipv6", rtr, input_dict, protocol=protocol)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_cost_tc52_p0(request):
+ """OSPF Cost - verifying ospf interface cost functionality"""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure ospf cost as 20 on interface between R0 and R1. "
+ "Configure ospf cost as 30 between interface between R0 and R2."
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 20}}, "r2": {"ospf6": {"cost": 30}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between"
+ " r0 and r1 as 30 and r0 and r2 as 20"
+ )
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Swap the costs between interfaces on r0, between r0 and r1 to 30"
+ ", r0 and r2 to 20"
+ )
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 30}}, "r2": {"ospf6": {"cost": 20}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0 "
+ "and r1 as 30 and r0 and r2 as 20."
+ )
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=r0_ospf_cost)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r1.")
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 30, "del_action": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 20}}}}
+ }
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ " and r1 as 10 and r0 and r2 as 20."
+ )
+
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(" Un configure cost from the interface r0 - r2.")
+
+ r0_ospf_cost = {
+ "r0": {"links": {"r2": {"ospf6": {"cost": 20, "del_action": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r0_ospf_cost)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that cost is updated in the ospf interface between r0"
+ "and r1 as 10 and r0 and r2 as 10"
+ )
+
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"cost": 10}}, "r2": {"ospf6": {"cost": 10}}}}
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_def_rte_tc9_p0(request):
+ """OSPF default route - Verify OSPF default route origination."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ global topo
+ step("Bring up the base config.")
+ step("Configure OSPF on all the routers of the topology.")
+ if tgen.routers_have_failure():
+ check_router_status(tgen)
+ reset_config_on_routers(tgen)
+
+ step(" Configure default-information originate always on R0.")
+ input_dict = {"r0": {"ospf6": {"default-information": {"originate": True}}}}
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ step(" Configure default-information originate always on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated in area always.")
+ dut = "r1"
+
+ step(" Configure default-information originate metric type 1 on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 1,
+ }
+ }
+ }
+ }
+
+ step(
+ "Verify that default route is originated in area when external "
+ "routes are present in R0 with metric type as 1."
+ )
+ dut = "r0"
+ step(
+ "Verify that on R1 default route with type 1 is installed"
+ " (R1 is DUT in this case)"
+ )
+ dut = "r1"
+ step("Configure default-information originate metric type 2 on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 2,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that default route is originated in area when external"
+ " routes are present in R0 with metric type as 2."
+ )
+
+ dut = "r1"
+ step(" Configure default-information originate metric 100 on R0")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "metric-type": 2,
+ "metric": 100,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated with cost as 100 on R0.")
+
+ dut = "r1"
+
+ step("Delete the default-information command")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ "delete": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ step("Configure default-information originate always on R0.")
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ "always": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Configure default route originate with active def route in zebra")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": "0::0/0",
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "ospf6": {
+ "default-information": {
+ "originate": True,
+ }
+ }
+ }
+ }
+ result = create_router_ospf(tgen, topo, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that default route is originated by R0.")
+ dut = "r1"
+
+ step("Delete static route")
+ input_dict = {
+ "r0": {
+ "static_routes": [
+ {
+ "network": "0::0/0",
+ "no_of_ip": 1,
+ "next_hop": "Null0",
+ "delete": True,
+ }
+ ]
+ }
+ }
+ result = create_static_routes(tgen, input_dict)
+ 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/ospfv3_basic_functionality/test_ospfv3_single_area.py b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
new file mode 100644
index 0000000..d318ec0
--- /dev/null
+++ b/tests/topotests/ospfv3_basic_functionality/test_ospfv3_single_area.py
@@ -0,0 +1,1342 @@
+#!/usr/bin/python
+
+#
+# Copyright (c) 2021 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.
+#
+
+
+"""OSPF Basic Functionality Automation."""
+import os
+import sys
+import time
+import pytest
+import ipaddress
+
+from copy import deepcopy
+from lib.topotest import frr_unicode
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ step,
+ create_interfaces_cfg,
+ topo_daemons,
+ create_debug_log_config,
+ apply_raw_config,
+)
+from lib.topolog import logger
+from lib.topojson import build_config_from_json
+
+from lib.ospf import (
+ verify_ospf6_neighbor,
+ clear_ospf,
+ verify_ospf6_interface,
+ create_router_ospf,
+ config_ospf6_interface,
+ verify_ospf6_rib,
+)
+
+from ipaddress import IPv6Address
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.staticd]
+
+
+# Global variables
+topo = None
+
+"""
+TOPOOLOGY =
+ Please view in a fixed-width font such as Courier.
+ +---+ A1 +---+
+ +R1 +------------+R2 |
+ +-+-+- +--++
+ | -- -- |
+ | -- A0 -- |
+ A0| ---- |
+ | ---- | A2
+ | -- -- |
+ | -- -- |
+ +-+-+- +-+-+
+ +R0 +-------------+R3 |
+ +---+ A3 +---+
+
+TESTCASES =
+1. OSPF IFSM -Verify state change events on p2p network.
+2. OSPF Timers - Verify OSPF interface timer hello interval functionality
+3. OSPF Timers - Verify OSPF interface timer dead interval functionality
+4. Verify ospf show commands with json output.
+5. Verify NFSM events when ospf nbr changes with different MTU values.
+ """
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/ospfv3_single_area.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # get list of daemons needs to be started for this suite.
+ daemons = topo_daemons(tgen, topo)
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons and then start routers
+ start_topology(tgen, daemons)
+
+ # Creating configuration from JSON
+ build_config_from_json(tgen, topo)
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+# ##################################
+# Test cases start here.
+# ##################################
+
+
+def test_ospfv3_p2p_tc3_p0(request):
+ """OSPF IFSM -Verify state change events on p2p network."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+ step(
+ "Verify that OSPF is subscribed to multi cast services "
+ "(All SPF, all DR Routers)."
+ )
+ step("Verify that interface is enabled in ospf.")
+ step("Verify that config is successful.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo["routers"]["r0"]["links"]["r3"]["ipv6"],
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str(
+ IPv6Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(intf_ip.split("/")[1])
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf6": {
+ "internetAddress": [
+ {
+ "type": "inet6",
+ "address": topo_modify_change_ip["routers"]["r0"][
+ "links"
+ ]["r3"]["ipv6"].split("/")[0],
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Modify the mask on the R0 interface")
+ ip_addr = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ mask = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ step("Delete the ip address")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": ip_addr,
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Change the ip on the R0 interface")
+
+ topo_modify_change_ip = deepcopy(topo)
+ intf_ip = topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"]
+ topo_modify_change_ip["routers"]["r0"]["links"]["r3"]["ipv6"] = str(
+ IPv6Address(frr_unicode(intf_ip.split("/")[0])) + 3
+ ) + "/{}".format(int(intf_ip.split("/")[1]) + 1)
+
+ build_config_from_json(tgen, topo_modify_change_ip, save_bkup=False)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ospf6": {
+ "internetAddress": [
+ {
+ "type": "inet6",
+ "address": topo_modify_change_ip["routers"]["r0"][
+ "links"
+ ]["r3"]["ipv6"].split("/")[0],
+ }
+ ],
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r3": {
+ "ipv6": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "ipv6"
+ ],
+ "interface": topo_modify_change_ip["routers"]["r0"]["links"]["r3"][
+ "interface"
+ ],
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ build_config_from_json(tgen, topo, save_bkup=False)
+
+ step("Change the area id on the interface")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.1"},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ input_dict = {
+ "r0": {
+ "links": {
+ "r3": {
+ "interface": topo["routers"]["r0"]["links"]["r3"]["interface"],
+ "ospf6": {"area": "0.0.0.0"},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that interface is enabled in ospf.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r3": {"ospf6": {}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify the all neighbors are up after clearing the process.")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_hello_tc10_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer hello interval functionality
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("modify hello timer from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 11,
+ "timerIntervalsConfigDead": 12,
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify hello timer from default value to r1 hello timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 11, "dead_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 11,
+ "timerIntervalsConfigDead": 12,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 10,
+ "timerIntervalsConfigDead": 40,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("reconfigure the default hello timer value to default on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 10, "dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 10,
+ "timerIntervalsConfigDead": 40,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step("configure hello timer = 1 on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 1, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 1,
+ "timerIntervalsConfigDead": 4,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "setup_module :Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Configure hello timer = 65535")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 65535, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 65535, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {
+ "links": {
+ "r1": {
+ "ospf6": {
+ "timerIntervalsConfigHello": 65535,
+ "timerIntervalsConfigDead": 4,
+ }
+ }
+ }
+ }
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+ step(" Try configuring timer values outside range for example 65536")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"hello_interval": 65536, "dead_interval": 4},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Create interface failed. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Unconfigure the hello timer from the interface from r1 and r2.")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 65535},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that timer value is deleted from intf & " "set to default value 40 sec."
+ )
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigHello": 10}}}}}
+ dut = "r1"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_dead_tc11_p0(request):
+ """
+ OSPF timers.
+
+ Verify OSPF interface timer dead interval functionality
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step("Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("modify dead interval from default value to some other value on r1")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"hello_interval": 12, "dead_interval": 48},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "verify that new timer value is configured and applied using "
+ "the show ip ospf interface command."
+ )
+ dut = "r1"
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 48}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("modify dead interval from default value to r1" "dead interval timer on r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 48, "hello_interval": 12},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 48}}}}}
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step("remove ospf on R0")
+ ospf_del = {"r0": {"ospf6": {"delete": True}}}
+ result = create_router_ospf(tgen, topo, ospf_del)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ # reconfiguring deleted ospf process by resetting the configs.
+ reset_config_on_routers(tgen)
+
+ step("reconfigure the default dead interval timer value to " "default on r1 and r2")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 40},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step(" Configure dead timer = 65535 on r1 and r2")
+
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that new timer value is configured.")
+ input_dict = {
+ "r0": {"links": {"r1": {"ospf6": {"timerIntervalsConfigDead": 65535}}}}
+ }
+ dut = "r0"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("verify that ospf neighbours are full")
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, dut=dut)
+ assert ospf_covergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, ospf_covergence
+ )
+
+ step(" Try configuring timer values outside range for example 65536")
+ topo1 = {
+ "r0": {
+ "links": {
+ "r1": {
+ "interface": topo["routers"]["r0"]["links"]["r1"]["interface"],
+ "ospf6": {"dead_interval": 65536},
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n Create interface config failed. Error: {}".format(
+ tc_name, result
+ )
+
+ step("Unconfigure the dead timer from the interface from r1 and r2.")
+
+ topo1 = {
+ "r1": {
+ "links": {
+ "r0": {
+ "interface": topo["routers"]["r1"]["links"]["r0"]["interface"],
+ "ospf6": {"dead_interval": 65535},
+ "delete": True,
+ }
+ }
+ }
+ }
+
+ result = create_interfaces_cfg(tgen, topo1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Verify that timer value is deleted from intf & " "set to default value 40 sec."
+ )
+ input_dict = {"r1": {"links": {"r0": {"ospf6": {"timerIntervalsConfigDead": 40}}}}}
+ dut = "r1"
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_tc4_mtu_ignore_p0(request):
+ """
+ OSPF NFSM - MTU change
+
+ Verify NFSM events when ospf nbr changes with different MTU values
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step(" Bring up the base config as per the topology")
+ step("Configure OSPF on all the routers of the topology.")
+ step("Verify that OSPF neighbors are FULL.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ rtr0 = tgen.routers()["r0"]
+ rtr1 = tgen.routers()["r1"]
+
+ r0_r1_intf = topo["routers"]["r0"]["links"]["r1"]["interface"]
+ r1_r0_intf = topo["routers"]["r1"]["links"]["r0"]["interface"]
+
+ rtr0.run("ifconfig {} mtu 1400".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+ clear_ospf(tgen, "r1", ospf="ospf6")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step(
+ "Verify that configured MTU value is updated in the show ip " "ospf interface."
+ )
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 1400}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Modify the MTU to non default Value on R0 to R1 interface. "
+ "Reset ospf neighbors on R0."
+ )
+ rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure mtu ignore and change the value of the mtu to non default"
+ " on R0 to R1 interface. Reset ospf neighbors on R0."
+ )
+ r0_ospf_mtu = {"r0": {"links": {"r1": {"ospf6": {"mtu_ignore": True}}}}}
+ result = config_ospf6_interface(tgen, topo, r0_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"mtuMismatchDetection": True}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ r1_ospf_mtu = {"r1": {"links": {"r0": {"ospf6": {"mtu_ignore": True}}}}}
+ result = config_ospf6_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ rtr0.run("ifconfig {} mtu 1400".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Unconfigure mtu-ignore command from the interface. "
+ "Reset ospf neighbors on R0."
+ )
+
+ r1_ospf_mtu = {
+ "r1": {"links": {"r0": {"ospf6": {"mtu_ignore": True, "delete": True}}}}
+ }
+ result = config_ospf6_interface(tgen, topo, r1_ospf_mtu)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(tc_name, result)
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step(
+ "Verify that OSPF neighborship between R0 and R1 is stuck in Exstart" " State."
+ )
+ result = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \n OSPF nbrs are Full "
+ "instead of Exstart. Error: {}".format(tc_name, result)
+ )
+
+ step("Modify the MTU to again default valaue on R0 to R1 interface.")
+
+ rtr0.run("ifconfig {} mtu 1500".format(r0_r1_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step(
+ "Configure ospf interface with jumbo MTU (9216)." "Reset ospf neighbors on R0."
+ )
+
+ rtr0.run("ifconfig {} mtu 9216".format(r0_r1_intf))
+ rtr1.run("ifconfig {} mtu 9216".format(r1_r0_intf))
+
+ clear_ospf(tgen, "r0", ospf="ospf6")
+ clear_ospf(tgen, "r1", ospf="ospf6")
+
+ step("Verify that OSPF neighborship between R0 and R1 becomes full.")
+ result = verify_ospf6_neighbor(tgen, topo)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ step("Verify that jumbo MTU is updated in the show ip ospf interface.")
+ dut = "r0"
+ input_dict = {"r0": {"links": {"r1": {"ospf6": {"interfaceMtu": 9216}}}}}
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def test_ospfv3_show_p1(request):
+ """Verify ospf show commands with json output."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ input_dict = {"r2": {"debug": {"log_file": "debug.log", "enable": ["ospf6"]}}}
+
+ result = create_debug_log_config(tgen, input_dict)
+
+ # Code coverage steps #Do Not upstream
+ input_dict_config = {
+ "r1": {
+ "raw_config": [
+ "end",
+ "debug ospf6 event",
+ "debug ospf6 gr helper",
+ "debug ospf6 ism events",
+ "debug ospf6 ism status",
+ "debug ospf6 ism timers",
+ "debug ospf6 nsm events",
+ "debug ospf6 nsm status",
+ "debug ospf6 nsm timers ",
+ "debug ospf6 nssa",
+ "debug ospf6 lsa aggregate",
+ "debug ospf6 lsa flooding ",
+ "debug ospf6 lsa generate",
+ "debug ospf6 lsa install ",
+ "debug ospf6 lsa refresh",
+ "debug ospf6 packet all detail",
+ "debug ospf6 packet all recv",
+ "debug ospf6 packet all send",
+ "debug ospf6 packet dd detail",
+ "debug ospf6 packet dd recv",
+ "debug ospf6 packet dd send ",
+ "debug ospf6 packet hello detail",
+ "debug ospf6 packet hello recv",
+ "debug ospf6 packet hello send",
+ "debug ospf6 packet ls-ack detail",
+ "debug ospf6 packet ls-ack recv",
+ "debug ospf6 packet ls-ack send",
+ "debug ospf6 packet ls-request detail",
+ "debug ospf6 packet ls-request recv",
+ "debug ospf6 packet ls-request send",
+ "debug ospf6 packet ls-update detail",
+ "debug ospf6 packet ls-update recv",
+ "debug ospf6 packet ls-update send",
+ "debug ospf6 sr",
+ "debug ospf6 te ",
+ "debug ospf6 zebra interface",
+ "debug ospf6 zebra redistribute",
+ ]
+ }
+ }
+
+ apply_raw_config(tgen, input_dict_config)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ dut = "r1"
+ input_dict = {
+ "r1": {
+ "links": {
+ "r0": {
+ "ospf6": {
+ "status": "up",
+ "type": "BROADCAST",
+ "attachedToArea": True,
+ "instanceId": 0,
+ "interfaceMtu": 1500,
+ "autoDetect": 1500,
+ "mtuMismatchDetection": "enabled",
+ "areaId": "0.0.0.0",
+ "cost": 10,
+ "transmitDelaySec": 1,
+ "priority": 1,
+ "timerIntervalsConfigHello": 1,
+ "timerIntervalsConfigDead": 4,
+ "timerIntervalsConfigRetransmit": 5,
+ "dr": "0.0.0.0",
+ "bdr": "0.0.0.0",
+ "numberOfInterfaceScopedLsa": 2,
+ "pendingLsaLsUpdateCount": 0,
+ "lsUpdateSendThread": "off",
+ "pendingLsaLsAckCount": 0,
+ "lsAckSendThread": "off",
+ }
+ }
+ }
+ }
+ }
+ result = verify_ospf6_interface(tgen, topo, dut=dut, input_dict=input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ ip = topo["routers"]["r0"]["links"]["r3"]["ipv6"]
+ ip_net = str(ipaddress.ip_interface(u"{}".format(ip)).network)
+ nh = topo["routers"]["r0"]["links"]["r1"]["ipv6"].split("/")[0]
+ input_dict = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_net, "no_of_ip": 1, "routeType": "Network"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_ospf6_rib(tgen, dut, input_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(tc_name, result)
+
+ write_test_footer(tc_name)
+
+
+def ospfv3_router_id_tc14_p2(request):
+ """OSPF Router ID - Verify OSPF router id changes."""
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ global topo
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ step("Configure system router id as 1.1.1.1 on R1 , clear ospf router")
+ ospf_rid = {"r0": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("configure ospf router id as 1.1.1.2 on R1, clear ospf router")
+ ospf_rid = {"r1": {"ospf6": {"router_id": "1.1.1.2"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ topo1 = deepcopy(topo)
+ step("Verify that OSPF takes system router ID as ospf router id.")
+
+ topo1["routers"]["r0"]["ospf6"]["router_id"] = "1.1.1.1"
+ topo1["routers"]["r1"]["ospf6"]["router_id"] = "1.1.1.2"
+
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" delete ospf router id and clear ospf process.")
+ ospf_rid = {"r0": {"ospf6": {"del_router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_rid = {"r1": {"ospf6": {"del_router_id": "1.1.1.2"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ reset_config_on_routers(tgen)
+
+ step(" Configure R0 R1 R2 with same router ids")
+ ospf_rid = {"r0": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ step("configure ospf router id as 1.1.1.2 on R1, reboot router")
+ ospf_rid = {"r1": {"ospf6": {"router_id": "1.1.1.1"}}}
+ result = create_router_ospf(tgen, topo, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo, expected=False)
+ assert (
+ ospf_covergence is not True
+ ), "OSPF NBRs are up.Failed \n Error:" " {}".format(ospf_covergence)
+ topo1 = {}
+ topo1 = deepcopy(topo)
+
+ for rtr in ["r1", "r2", "r3", "r0"]:
+ topo1["routers"][rtr]["ospf6"].pop("router_id")
+
+ build_config_from_json(tgen, topo1, save_bkup=False)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is not True, (
+ "Testcase {} :Failed \n Neighborship "
+ "should not up as no router id is configured. Error: {}".format(tc_name, result)
+ )
+
+ step("Clear ospf process and check nbrs should not be up.")
+ for rtr in topo["routers"]:
+ clear_ospf(tgen, rtr, ospf="ospf6")
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is not True, (
+ "Testcase {} :Failed \n Neighborship "
+ "should not up as no router id is configured. Error: {}".format(tc_name, result)
+ )
+
+ topo1 = deepcopy(topo)
+
+ step("Configure system router id on routers , clear ospf router")
+ ospf_rid = {
+ "r0": {"ospf6": {"router_id": "1.1.1.1"}},
+ "r1": {"ospf6": {"router_id": "1.1.1.2"}},
+ "r2": {"ospf6": {"router_id": "1.1.1.3"}},
+ "r3": {"ospf6": {"router_id": "1.1.1.4"}},
+ }
+ result = create_router_ospf(tgen, topo1, ospf_rid)
+ assert result is True, "Testcase : Failed \n Error: {}".format(result)
+
+ topo1["routers"]["r0"]["ospf6"]["router_id"] = "1.1.1.1"
+ topo1["routers"]["r1"]["ospf6"]["router_id"] = "1.1.1.2"
+ topo1["routers"]["r2"]["ospf6"]["router_id"] = "1.1.1.3"
+ topo1["routers"]["r3"]["ospf6"]["router_id"] = "1.1.1.4"
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo1)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ step(" Bring up the base config as per the topology")
+ reset_config_on_routers(tgen)
+
+ ospf_covergence = verify_ospf6_neighbor(tgen, topo)
+ assert ospf_covergence is True, "OSPF NBRs not up.Failed \n Error:" " {}".format(
+ ospf_covergence
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pbr_topo1/__init__.py b/tests/topotests/pbr_topo1/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/pbr_topo1/__init__.py
diff --git a/tests/topotests/pbr_topo1/r1/linux-rules.json b/tests/topotests/pbr_topo1/r1/linux-rules.json
new file mode 100644
index 0000000..5af4363
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/linux-rules.json
@@ -0,0 +1,19 @@
+[
+ {
+ "iif": "r1-eth1",
+ "pref": "304",
+ "from": "4.5.6.7"
+ },
+ {
+ "to": "3.4.5.0/24",
+ "iif": "r1-eth2",
+ "pref": "304",
+ "from": "1.2.0.0/16"
+ },
+ {
+ "to": "9.9.9.9",
+ "iif": "r1-eth1",
+ "pref": "309",
+ "from": "all"
+ }
+]
diff --git a/tests/topotests/pbr_topo1/r1/pbr-interface.json b/tests/topotests/pbr_topo1/r1/pbr-interface.json
new file mode 100644
index 0000000..e28d9fb
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/pbr-interface.json
@@ -0,0 +1,27 @@
+[
+ {
+ "name":"r1-eth1",
+ "policy":"EVA",
+ "valid":true
+ },
+ {
+ "name":"r1-eth2",
+ "policy":"DONNA",
+ "valid":true
+ },
+ {
+ "name":"r1-eth3",
+ "policy":"AKIHABARA",
+ "valid":true
+ },
+ {
+ "name":"r1-eth4",
+ "policy":"ASAKUSA",
+ "valid":true
+ },
+ {
+ "name":"r1-noexist",
+ "policy":"NOEXIST",
+ "valid":false
+ }
+]
diff --git a/tests/topotests/pbr_topo1/r1/pbr-map.json b/tests/topotests/pbr_topo1/r1/pbr-map.json
new file mode 100644
index 0000000..bfa0ecb
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/pbr-map.json
@@ -0,0 +1,152 @@
+[
+ {
+ "name":"AKIHABARA",
+ "valid":false,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup": {
+ "name":"C",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"192.168.4.0\/24"
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Invalid Src or Dst",
+ "nexthopGroup":{
+ "name":"C",
+ "installed":true,
+ "installedInternally":1
+ }
+ },
+ {
+ "sequenceNumber":15,
+ "vrfUnchanged":false,
+ "installed":false,
+ "installedReason":"No Nexthops"
+ }
+ ]
+ },
+ {
+ "name":"ASAKUSA",
+ "valid":true,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "matchDst":"c0ff:ee::\/64",
+ "nexthopGroup":{
+ "name":"D",
+ "installed":true,
+ "installedInternally":1
+ }
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"ASAKUSA10",
+ "installed":true,
+ "installedInternally":1
+ },
+ "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
+ }
+ ]
+ },
+ {
+ "name":"DONNA",
+ "valid":false,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":false,
+ "installedReason":"Invalid NH-group",
+ "nexthopGroup":{
+ "name":"B",
+ "installed":false,
+ "installedInternally":0
+ },
+ "matchSrc":"1.2.0.0\/16",
+ "matchDst":"3.4.5.0\/24"
+ },
+ {
+ "sequenceNumber":10,
+ "vrfUnchanged":true,
+ "installed":false,
+ "installedReason":"Valid",
+ "matchSrc":"1.2.0.0\/16",
+ "matchDst":"3.4.5.0\/24"
+ }
+ ]
+ },
+ {
+ "name":"EVA",
+ "valid":true,
+ "policies":[
+ {
+ "sequenceNumber":5,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"EVA5",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchSrc":"4.5.6.7\/32"
+ },
+ {
+ "sequenceNumber":10,
+ "ruleNumber":309,
+ "vrfUnchanged":false,
+ "installed":true,
+ "installedReason":"Valid",
+ "nexthopGroup":{
+ "name":"A",
+ "installed":true,
+ "installedInternally":1
+ },
+ "matchDst":"9.9.9.9\/32"
+ }
+ ]
+ }
+]
diff --git a/tests/topotests/pbr_topo1/r1/pbr-nexthop-groups.json b/tests/topotests/pbr_topo1/r1/pbr-nexthop-groups.json
new file mode 100644
index 0000000..540ea28
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/pbr-nexthop-groups.json
@@ -0,0 +1,95 @@
+[
+ {
+ "name":"ASAKUSA10",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"c0ff:ee::1",
+ "valid":true
+ }
+ ]
+ },
+ {
+ "name":"A",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"192.168.2.2",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.3.2",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.1.2",
+ "valid":true
+ }
+ ]
+ },
+ {
+ "name":"D",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"c0ff:ee::3",
+ "valid":true
+ },
+ {
+ "nexthop":"c0ff:ee::2",
+ "valid":true
+ },
+ {
+ "nexthop":"c0ff:ee::1",
+ "valid":true
+ }
+ ]
+ },
+ {
+ "name":"C",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"192.168.4.3",
+ "targetVrf":"vrf-chiyoda",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.4.2",
+ "targetVrf":"vrf-chiyoda",
+ "valid":true
+ },
+ {
+ "nexthop":"192.168.4.1",
+ "targetVrf":"vrf-chiyoda",
+ "valid":true
+ }
+ ]
+ },
+ {
+ "name":"B",
+ "valid":false,
+ "installed":false,
+ "nexthops":[
+ {
+ "nexthop":"192.168.50.1",
+ "valid":false
+ }
+ ]
+ },
+ {
+ "name":"EVA5",
+ "valid":true,
+ "installed":true,
+ "nexthops":[
+ {
+ "nexthop":"192.168.1.5",
+ "valid":true
+ }
+ ]
+ }
+]
diff --git a/tests/topotests/pbr_topo1/r1/pbrd.conf b/tests/topotests/pbr_topo1/r1/pbrd.conf
new file mode 100644
index 0000000..2a5f464
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/pbrd.conf
@@ -0,0 +1,100 @@
+! debug pbr
+! debug pbr events
+! debug pbr nht
+! debug pbr zebra
+# Valid table range
+pbr table range 10000 50000
+# Try to set invalid bounds
+pbr table range 10000 10001
+pbr table range 50000 10000
+# Reset table range
+no pbr table range
+!
+nexthop-group A
+ nexthop 192.168.1.2
+ nexthop 192.168.2.2
+ nexthop 192.168.3.2
+!
+# This one is bogus and should
+# never work
+nexthop-group B
+ nexthop 192.168.50.1
+!
+nexthop-group C
+ nexthop 192.168.4.1 nexthop-vrf vrf-chiyoda
+ nexthop 192.168.4.2 nexthop-vrf vrf-chiyoda
+ nexthop 192.168.4.3 nexthop-vrf vrf-chiyoda
+!
+nexthop-group D
+ nexthop c0ff:ee::1
+ nexthop c0ff:ee::2
+ nexthop c0ff:ee::3
+!
+pbr-map EVA seq 5
+ match src-ip 4.5.6.7/32
+ set nexthop 192.168.1.5
+!
+pbr-map EVA seq 10
+ match dst-ip 9.9.9.9/32
+ set nexthop-group A
+!
+pbr-map DONNA seq 5
+ match dst-ip 3.4.5.0/24
+ match src-ip 1.2.0.0/16
+ set nexthop-group B
+!
+pbr-map DONNA seq 10
+ match dst-ip 3.4.5.0/24
+ match src-ip 1.2.0.0/16
+ set vrf unchanged
+!
+pbr-map AKIHABARA seq 5
+ no set vrf unchanged
+ match dst-ip 192.168.4.0/24
+ set nexthop-group C
+!
+pbr-map AKIHABARA seq 10
+ match dst-ip 192.168.4.0/24
+ no match dst-ip 192.168.4.0/24
+ set nexthop-group C
+!
+pbr-map AKIHABARA seq 15
+ set vrf noexist-vrf
+ match dst-ip 192.168.4.0/24
+ set nexthop-group C
+ no set nexthop-group C
+!
+pbr-map ASAKUSA seq 5
+ match dst-ip c0ff:ee::/64
+ set nexthop-group D
+!
+pbr-map ASAKUSA seq 10
+ match dst-ip dead:beef::/64
+ 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
+!
+int r1-eth2
+ pbr-policy DONNA
+!
+int r1-eth3
+ pbr-policy AKIHABARA
+!
+int r1-eth4
+ pbr-policy ASAKUSA
+!
+int r1-noexist
+ pbr-policy NOEXIST
diff --git a/tests/topotests/pbr_topo1/r1/zebra.conf b/tests/topotests/pbr_topo1/r1/zebra.conf
new file mode 100644
index 0000000..2ec947c
--- /dev/null
+++ b/tests/topotests/pbr_topo1/r1/zebra.conf
@@ -0,0 +1,14 @@
+int r1-eth0
+ ip address 192.168.1.1/24
+
+int r1-eth1
+ ip address 192.168.2.1/24
+
+int r1-eth2
+ ip address 192.168.3.1/24
+
+int r1-eth3 vrf vrf-chiyoda
+ ip address 192.168.4.1/24
+
+int r1-eth4
+ ipv6 address c0ff:ee::/64
diff --git a/tests/topotests/pbr_topo1/test_pbr_topo1.py b/tests/topotests/pbr_topo1/test_pbr_topo1.py
new file mode 100644
index 0000000..8506a15
--- /dev/null
+++ b/tests/topotests/pbr_topo1/test_pbr_topo1.py
@@ -0,0 +1,288 @@
+#!/usr/bin/env python
+
+#
+# test_pbr_topo1.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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_pbr_topo1.py: Testing PBR
+
+"""
+
+import os
+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.
+
+pytestmark = [pytest.mark.pbrd]
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # Populate routers
+ for routern in range(1, 2):
+ tgen.add_router("r{}".format(routern))
+
+ # Populate switches
+ for switchn in range(1, 6):
+ switch = tgen.add_switch("sw{}".format(switchn))
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ krel = platform.release()
+ if topotest.version_cmp(krel, "4.10") < 0:
+ tgen.errors = "Newer kernel than 4.9 needed for pbr tests"
+ pytest.skip(tgen.errors)
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ # Install vrf into the kernel and slave eth3
+ router.run("ip link add vrf-chiyoda type vrf table 1000")
+ router.run("ip link set dev {}-eth3 master vrf-chiyoda".format(rname))
+ router.run("ip link set vrf-chiyoda up")
+
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PBRD, os.path.join(CWD, "{}/pbrd.conf".format(rname))
+ )
+
+ 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_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ topotest.sleep(5, "Waiting for PBR convergence")
+
+
+def test_pbr_data():
+ "Test PBR 'show ip eigrp'"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Verify PBR Status
+ logger.info("Verifying PBR routes")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name)
+ logger.info(intf_file)
+
+ # Read expected result from file
+ expected = json.loads(open(intf_file).read())
+
+ # Actual output from router
+ test_func = partial(
+ topotest.router_json_cmp, router, "show pbr interface json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"show pbr interface" mismatches on {}'.format(router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+ map_file = "{}/{}/pbr-map.json".format(CWD, router.name)
+ logger.info(map_file)
+
+ # Read expected result from file
+ expected = json.loads(open(map_file).read())
+
+ # Actual output from router
+ test_func = partial(
+ topotest.router_json_cmp, router, "show pbr map json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"show pbr map" mismatches on {}'.format(router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+ nexthop_file = "{}/{}/pbr-nexthop-groups.json".format(CWD, router.name)
+ logger.info(nexthop_file)
+
+ # Read expected result from file
+ expected = json.loads(open(nexthop_file).read())
+
+ # Actual output from router
+ test_func = partial(
+ topotest.router_json_cmp, router, "show pbr nexthop-groups json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"show pbr nexthop-groups" mismatches on {}'.format(router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+
+def test_pbr_flap():
+ "Test PBR interface flapping"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Verify PBR Status
+ logger.info("Flapping PBR Interfaces")
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ # Flap interface to see if route-map properties are intact
+ # Shutdown interface
+
+ for i in range(5):
+ intf = "r1-eth{}".format(i)
+
+ # Down and back again
+ shutdown_bringup_interface(tgen, router.name, intf, False)
+ shutdown_bringup_interface(tgen, router.name, intf, True)
+
+ intf_file = "{}/{}/pbr-interface.json".format(CWD, router.name)
+ logger.info(intf_file)
+
+ # Read expected result from file
+ expected = json.loads(open(intf_file).read())
+
+ # Actual output from router
+ test_func = partial(
+ topotest.router_json_cmp, router, "show pbr interface json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=30, wait=1)
+ assertmsg = '"show pbr interface" mismatches on {}'.format(router.name)
+ if result is not None:
+ gather_pbr_data_on_error(router)
+ assert result is None, assertmsg
+
+
+def test_rule_linux_installation():
+ "Ensure that rule is installed in the kernel"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking for installed PBR rules in OS")
+
+ def _get_router_rules(router, expected):
+ actual = topotest.ip_rules(router)
+
+ logger.info(actual)
+ return topotest.json_cmp(actual, expected)
+
+ router_list = tgen.routers().values()
+ for router in router_list:
+ rules_file = "{}/{}/linux-rules.json".format(CWD, router.name)
+
+ expected = json.loads(open(rules_file).read())
+
+ test_func = partial(_get_router_rules, router, expected)
+
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assertmsg = "Router {} OS rules mismatch".format(router.name)
+ assert result is None, assertmsg
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
+
+#
+# EXTRA SAUCE
+#
+def gather_pbr_data_on_error(router):
+ logger.info(router.vtysh_cmd("show ip route"))
+ logger.info(router.vtysh_cmd("show ip route vrf vrf-chiyoda"))
+ logger.info(router.vtysh_cmd("show ip nht"))
+ logger.info(router.vtysh_cmd("show pbr interface"))
+ logger.info(router.vtysh_cmd("show pbr map"))
+ logger.info(router.vtysh_cmd("show pbr nexthop-groups"))
+ logger.info(router.vtysh_cmd("show nexthop-group rib singleton ip"))
+ logger.info(router.vtysh_cmd("show nexthop-group rib singleton ipv6"))
+ logger.info(router.vtysh_cmd("show nexthop-group rib"))
+ logger.info(router.run("ip nexthop show"))
+ logger.info(router.run("ip route show"))
+ logger.info(router.run("ip route show table 1000"))
+ logger.info(router.run("ip route show table 10000"))
+ logger.info(router.run("ip -6 route show table 10000"))
+ logger.info(router.run("ip route show table 10001"))
+ logger.info(router.run("ip -6 route show table 10001"))
+ logger.info(router.run("ip route show table 10002"))
+ logger.info(router.run("ip -6 route show table 10002"))
+ logger.info(router.run("ip route show table 10003"))
+ logger.info(router.run("ip -6 route show table 10003"))
+ logger.info(router.run("ip route show table 10004"))
+ logger.info(router.run("ip -6 route show table 10004"))
+ logger.info(router.run("ip route show table 10005"))
+ logger.info(router.run("ip -6 route show table 10005"))
+ logger.info(router.run("ip rule show"))
diff --git a/tests/topotests/pim_acl/h1/zebra.conf b/tests/topotests/pim_acl/h1/zebra.conf
new file mode 100644
index 0000000..3d6540d
--- /dev/null
+++ b/tests/topotests/pim_acl/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_acl/h2/zebra.conf b/tests/topotests/pim_acl/h2/zebra.conf
new file mode 100644
index 0000000..95342f9
--- /dev/null
+++ b/tests/topotests/pim_acl/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r1/acl_1_pim_join.json b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
new file mode 100644
index 0000000..1b44b2b
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_1_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_2_pim_join.json b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
new file mode 100644
index 0000000..c020a48
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_2_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_3_pim_join.json b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
new file mode 100644
index 0000000..6122f73
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_3_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_4_pim_join.json b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
new file mode 100644
index 0000000..5f72256
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_4_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_5_pim_join.json b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
new file mode 100644
index 0000000..70021bd
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_5_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/acl_6_pim_join.json b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
new file mode 100644
index 0000000..2baac6c
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/acl_6_pim_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospf_neighbor.json b/tests/topotests/pim_acl/r1/ospf_neighbor.json
new file mode 100644
index 0000000..af83d6b
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospf_neighbor.json
@@ -0,0 +1,59 @@
+{
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "converged":"Full",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.12":[
+ {
+ "priority":0,
+ "converged":"Full",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.13":[
+ {
+ "priority":0,
+ "converged":"Full",
+ "address":"192.168.101.13",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.14":[
+ {
+ "priority":0,
+ "converged":"Full",
+ "address":"192.168.101.14",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ],
+ "192.168.0.15":[
+ {
+ "priority":0,
+ "converged":"Full",
+ "address":"192.168.101.15",
+ "ifaceName":"r1-eth1:192.168.101.1",
+ "retransmitCounter":0,
+ "requestCounter":0,
+ "dbSummaryCounter":0
+ }
+ ]
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/ospfd.conf b/tests/topotests/pim_acl/r1/ospfd.conf
new file mode 100644
index 0000000..c453dec
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/ospfd.conf
@@ -0,0 +1,16 @@
+hostname r1
+!
+! debug ospf event
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf
+ ospf router-id 192.168.0.1
+ passive-interface r1-eth0
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+
diff --git a/tests/topotests/pim_acl/r1/pim_neighbor.json b/tests/topotests/pim_acl/r1/pim_neighbor.json
new file mode 100644
index 0000000..ae95e8d
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pim_neighbor.json
@@ -0,0 +1,31 @@
+{
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.12":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ },
+ "192.168.101.15":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.15",
+ "drPriority":1
+ },
+ "192.168.101.14":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.14",
+ "drPriority":1
+ },
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ },
+ "192.168.101.13":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.13",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r1/pimd.conf b/tests/topotests/pim_acl/r1/pimd.conf
new file mode 100644
index 0000000..5bdfbea
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/pimd.conf
@@ -0,0 +1,31 @@
+hostname r1
+!
+! debug igmp events
+! debug igmp packets
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.11 prefix-list rp-pl-1
+ip pim rp 192.168.0.12 prefix-list rp-pl-2
+ip pim rp 192.168.0.13 prefix-list rp-pl-3
+ip pim rp 192.168.0.14 prefix-list rp-pl-4
+ip pim rp 192.168.0.15 prefix-list rp-pl-5
+ip pim join-prune-interval 5
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+ip prefix-list rp-pl-1 seq 10 permit 239.100.0.0/28
+ip prefix-list rp-pl-2 seq 10 permit 239.100.0.17/32
+ip prefix-list rp-pl-3 seq 10 permit 239.100.0.32/27
+ip prefix-list rp-pl-4 seq 10 permit 239.100.0.128/25
+ip prefix-list rp-pl-4 seq 20 permit 239.100.0.96/28
+ip prefix-list rp-pl-5 seq 10 permit 239.100.0.64/28
diff --git a/tests/topotests/pim_acl/r1/zebra.conf b/tests/topotests/pim_acl/r1/zebra.conf
new file mode 100644
index 0000000..74feb8f
--- /dev/null
+++ b/tests/topotests/pim_acl/r1/zebra.conf
@@ -0,0 +1,18 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface lo
+ ip address 192.168.0.1/32
+!
+interface r1-eth0
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1
+ description connection to r11/12/13/14/15 via sw2
+ ip address 192.168.101.1/24
+!
diff --git a/tests/topotests/pim_acl/r11/acl_1_pim_join.json b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
new file mode 100644
index 0000000..289bf51
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/acl_1_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r11/ospfd.conf b/tests/topotests/pim_acl/r11/ospfd.conf
new file mode 100644
index 0000000..86fb66d
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+! debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r11/pimd.conf b/tests/topotests/pim_acl/r11/pimd.conf
new file mode 100644
index 0000000..3e409dd
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r11/zebra.conf b/tests/topotests/pim_acl/r11/zebra.conf
new file mode 100644
index 0000000..137706d
--- /dev/null
+++ b/tests/topotests/pim_acl/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r12/acl_2_pim_join.json b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
new file mode 100644
index 0000000..76ab7ee
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/acl_2_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.17":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.17",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r12/ospfd.conf b/tests/topotests/pim_acl/r12/ospfd.conf
new file mode 100644
index 0000000..1110df1
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+! debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r12/pimd.conf b/tests/topotests/pim_acl/r12/pimd.conf
new file mode 100644
index 0000000..2fc853b
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.17/32
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r12/zebra.conf b/tests/topotests/pim_acl/r12/zebra.conf
new file mode 100644
index 0000000..bede104
--- /dev/null
+++ b/tests/topotests/pim_acl/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r13/acl_3_pim_join.json b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
new file mode 100644
index 0000000..48ad72c
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/acl_3_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r13-eth0":{
+ "name":"r13-eth0",
+ "state":"up",
+ "address":"192.168.101.13",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.32":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.32",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r13/ospfd.conf b/tests/topotests/pim_acl/r13/ospfd.conf
new file mode 100644
index 0000000..aff24c7
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r13
+!
+! debug ospf event
+!
+interface r13-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.13
+ network 192.168.0.13/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r13/pimd.conf b/tests/topotests/pim_acl/r13/pimd.conf
new file mode 100644
index 0000000..5e44879
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/pimd.conf
@@ -0,0 +1,17 @@
+hostname r13
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.13 239.100.0.32/27
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r13-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r13/zebra.conf b/tests/topotests/pim_acl/r13/zebra.conf
new file mode 100644
index 0000000..f9ff27a
--- /dev/null
+++ b/tests/topotests/pim_acl/r13/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r13
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.13/32
+!
+interface r13-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.13/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r14/acl_4_pim_join.json b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
new file mode 100644
index 0000000..46d86dd
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_4_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.255":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.255",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/acl_5_pim_join.json b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
new file mode 100644
index 0000000..2b291a8
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/acl_5_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r14-eth0":{
+ "name":"r14-eth0",
+ "state":"up",
+ "address":"192.168.101.14",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.97":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.97",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r14/ospfd.conf b/tests/topotests/pim_acl/r14/ospfd.conf
new file mode 100644
index 0000000..e5cf8e2
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r14
+!
+! debug ospf event
+!
+interface r14-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.14
+ network 192.168.0.14/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r14/pimd.conf b/tests/topotests/pim_acl/r14/pimd.conf
new file mode 100644
index 0000000..42beb2a
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/pimd.conf
@@ -0,0 +1,18 @@
+hostname r14
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.14 239.100.0.96/28
+ip pim rp 192.168.0.14 239.100.0.128/25
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r14-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r14/zebra.conf b/tests/topotests/pim_acl/r14/zebra.conf
new file mode 100644
index 0000000..8761b46
--- /dev/null
+++ b/tests/topotests/pim_acl/r14/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r14
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.14/32
+!
+interface r14-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.14/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/r15/acl_6_pim_join.json b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
new file mode 100644
index 0000000..05fed4e
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/acl_6_pim_join.json
@@ -0,0 +1,19 @@
+{
+ "r15-eth0":{
+ "name":"r15-eth0",
+ "state":"up",
+ "address":"192.168.101.15",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.70":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.70",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_acl/r15/ospfd.conf b/tests/topotests/pim_acl/r15/ospfd.conf
new file mode 100644
index 0000000..cc58325
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r15
+!
+! debug ospf event
+!
+interface r15-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 0
+!
+router ospf
+ ospf router-id 192.168.0.15
+ network 192.168.0.15/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_acl/r15/pimd.conf b/tests/topotests/pim_acl/r15/pimd.conf
new file mode 100644
index 0000000..d5a0450
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/pimd.conf
@@ -0,0 +1,17 @@
+hostname r15
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.15 239.100.0.64/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r15-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_acl/r15/zebra.conf b/tests/topotests/pim_acl/r15/zebra.conf
new file mode 100644
index 0000000..f6909dd
--- /dev/null
+++ b/tests/topotests/pim_acl/r15/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r15
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.15/32
+!
+interface r15-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.15/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_acl/test_pim_acl.py b/tests/topotests/pim_acl/test_pim_acl.py
new file mode 100755
index 0000000..a4e6630
--- /dev/null
+++ b/tests/topotests/pim_acl/test_pim_acl.py
@@ -0,0 +1,345 @@
+#!/usr/bin/env python
+
+#
+# test_pim_acl.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_pim_acl.py: Test PIM with RP selection using ACLs
+"""
+
+# Test PIM RP selection with ACLs
+#
+# Testing RP selection with ACLs. R1 uses multiple ACLs
+# to select desired RPs (R11 to R15)
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1 and R11 - R15.
+# - test_pim_convergence()
+# Wait for PIM convergence on all routers. PIM is run on
+# R1 and R11 - R15.
+# - test_mcast_acl_1():
+# Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1 which
+# should use R11 as RP
+# Stop multicast after verification
+# - test_mcast_acl_2():
+# Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17 which
+# should use R12 as RP
+# Stop multicast after verification
+# - test_mcast_acl_3():
+# Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32 which
+# should use R13 as RP
+# Stop multicast after verification
+# - test_mcast_acl_4():
+# Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_5():
+# Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97 which
+# should use R14 as RP
+# Stop multicast after verification
+# - test_mcast_acl_6():
+# Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70 which
+# should use R15 as RP
+# Stop multicast after verification
+# - teardown_module()
+# shutdown topology
+#
+
+# XXX clean up in later commit to avoid conflict on rebase
+# pylint: disable=C0413
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
+ +-----------+ | +----------+
+ | | .1 | .11 | Host R11 |
++---------+ | R1 |---------+--------| PIM RP |
+| Host H1 | 192.168.100.0/24 | | 192.168.101.0/24 +----------+
+| receive |------------------| uses ACLs | | +----------+
+|IGMP JOIN| .10 .1 | to pick | | .12 | Host R12 |
++---------+ | RP | +--------| PIM RP |
+ | | | +----------+
+ +-----------+ | +----------+
+ | .13 | Host R13 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .14 | Host R14 |
+ +--------| PIM RP |
+ | +----------+
+ | +----------+
+ | .15 | Host R15 |
+ +--------| PIM RP |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+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 lib.pim import McastTesterHelper
+
+pytestmark = [pytest.mark.pimd, pytest.mark.ospfd]
+
+
+def build_topo(tgen):
+ for hostNum in range(1, 3):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 16):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 3):
+ tgen.add_switch("sw{}".format(swNum))
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+ tgen.gears["r12"].add_link(tgen.gears["sw2"])
+ tgen.gears["r13"].add_link(tgen.gears["sw2"])
+ tgen.gears["r14"].add_link(tgen.gears["sw2"])
+ tgen.gears["r15"].add_link(tgen.gears["sw2"])
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def setup_module(module):
+ logger.info("PIM RP ACL Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != "h":
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router R1 did not converge"
+ assert res is None, assertmsg
+
+
+def check_mcast_entry(entry, mcastaddr, pimrp):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info(
+ "Testing PIM RP selection for ACL {} entry using {}".format(entry, mcastaddr)
+ )
+
+ with McastTesterHelper(tgen) as helper:
+ helper.run("h2", ["--send=0.7", mcastaddr, "h2-eth0"])
+ helper.run("h1", [mcastaddr, "h1-eth0"])
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ # tgen.mininet_cli()
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/acl_{}_pim_join.json".format(entry))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {}".format(mcastaddr))
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router r1 did not show join status"
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/acl_{}_pim_join.json".format(pimrp, entry))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "PIM router {} did not get selected as the PIM RP".format(pimrp)
+ assert res is None, assertmsg
+
+ return
+
+
+def test_mcast_acl_1():
+ "Test 1st ACL entry 239.100.0.0/28 with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(1, "239.100.0.1", "r11")
+
+
+def test_mcast_acl_2():
+ "Test 2nd ACL entry 239.100.0.17/32 with 239.100.0.17"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(2, "239.100.0.17", "r12")
+
+
+def test_mcast_acl_3():
+ "Test 3rd ACL entry 239.100.0.32/27 with 239.100.0.32"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(3, "239.100.0.32", "r13")
+
+
+def test_mcast_acl_4():
+ "Test 4th ACL entry 239.100.0.128/25 with 239.100.0.255"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(4, "239.100.0.255", "r14")
+
+
+def test_mcast_acl_5():
+ "Test 5th ACL entry 239.100.0.96/28 with 239.100.0.97"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(5, "239.100.0.97", "r14")
+
+
+def test_mcast_acl_6():
+ "Test 6th ACL entry 239.100.0.64/28 with 239.100.0.70"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry(6, "239.100.0.70", "r15")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pim_basic/mcast-rx.py b/tests/topotests/pim_basic/mcast-rx.py
new file mode 100755
index 0000000..8853376
--- /dev/null
+++ b/tests/topotests/pim_basic/mcast-rx.py
@@ -0,0 +1,92 @@
+#!/usr/bin/env python3
+#
+# mcast-rx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+# 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 Cumulus Networks 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.
+#
+"""
+Subscribe to a multicast group so that the kernel sends an IGMP JOIN
+for the multicast group we subscribed to.
+"""
+
+import argparse
+import logging
+import re
+import os
+import socket
+import subprocess
+import struct
+import sys
+import time
+
+
+def ifname_to_ifindex(ifname):
+ output = subprocess.check_output(
+ "ip link show %s" % ifname, shell=True, universal_newlines=True
+ )
+ first_line = output.split("\n")[0]
+ re_index = re.search("^(\d+):", first_line)
+
+ if re_index:
+ return int(re_index.group(1))
+
+ log.error("Could not parse the ifindex for %s out of\n%s" % (ifname, first_line))
+ return None
+
+
+# Thou shalt be root
+if os.geteuid() != 0:
+ sys.stderr.write("ERROR: You must have root privileges\n")
+ sys.exit(1)
+
+
+logging.basicConfig(
+ level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s"
+)
+
+# Color the errors and warnings in red
+logging.addLevelName(
+ logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)
+)
+logging.addLevelName(
+ logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)
+)
+log = logging.getLogger(__name__)
+
+parser = argparse.ArgumentParser(description="Multicast RX utility")
+
+parser.add_argument("group", help="Multicast IP")
+parser.add_argument("ifname", help="Interface name")
+parser.add_argument("--port", help="UDP port", default=1000)
+parser.add_argument("--sleep", help="Time to sleep before we stop waiting", default=5)
+args = parser.parse_args()
+
+# Create the datagram socket
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+sock.bind((args.group, args.port))
+
+newpid = os.fork()
+
+if newpid == 0:
+ ifindex = ifname_to_ifindex(args.ifname)
+ mreq = struct.pack(
+ "=4sLL", socket.inet_aton(args.group), socket.INADDR_ANY, ifindex
+ )
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
+ time.sleep(float(args.sleep))
+ sock.close()
diff --git a/tests/topotests/pim_basic/mcast-tx.py b/tests/topotests/pim_basic/mcast-tx.py
new file mode 100755
index 0000000..88c2345
--- /dev/null
+++ b/tests/topotests/pim_basic/mcast-tx.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python3
+#
+# mcast-tx.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+#
+# 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 Cumulus Networks 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.
+#
+
+import argparse
+import logging
+import socket
+import struct
+import time
+import sys
+
+logging.basicConfig(
+ level=logging.DEBUG, format="%(asctime)s %(levelname)5s: %(message)s"
+)
+
+# Color the errors and warnings in red
+logging.addLevelName(
+ logging.ERROR, "\033[91m %s\033[0m" % logging.getLevelName(logging.ERROR)
+)
+logging.addLevelName(
+ logging.WARNING, "\033[91m%s\033[0m" % logging.getLevelName(logging.WARNING)
+)
+log = logging.getLogger(__name__)
+
+parser = argparse.ArgumentParser(description="Multicast packet generator")
+parser.add_argument("group", help="Multicast IP")
+parser.add_argument("ifname", help="Interface name")
+parser.add_argument("--port", type=int, help="UDP port number", default=1000)
+parser.add_argument("--ttl", type=int, help="time-to-live", default=20)
+parser.add_argument("--count", type=int, help="Packets to send", default=1)
+parser.add_argument("--interval", type=int, help="ms between packets", default=100)
+args = parser.parse_args()
+
+# Create the datagram socket
+sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+# IN.SO_BINDTODEVICE is not defined in some releases of python but it is 25
+# https://github.com/sivel/bonding/issues/10
+#
+# Bind our socket to ifname
+#
+# Note ugly python version incompatibility
+#
+if sys.version_info[0] > 2:
+ sock.setsockopt(
+ socket.SOL_SOCKET,
+ 25,
+ struct.pack("%ds" % len(args.ifname), args.ifname.encode("utf-8")),
+ )
+else:
+ sock.setsockopt(
+ socket.SOL_SOCKET, 25, struct.pack("%ds" % len(args.ifname), args.ifname)
+ )
+
+# We need to make sure our sendto() finishes before we close the socket
+sock.setblocking(1)
+
+# Set the time-to-live
+sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, struct.pack("b", args.ttl))
+
+ms = args.interval / 1000.0
+
+# Send data to the multicast group
+for x in range(args.count):
+ log.info(
+ "TX multicast UDP packet to %s:%d on %s" % (args.group, args.port, args.ifname)
+ )
+
+ #
+ # Note ugly python version incompatibility
+ #
+ if sys.version_info[0] > 2:
+ sent = sock.sendto(b"foobar %d" % x, (args.group, args.port))
+ else:
+ sent = sock.sendto("foobar %d" % x, (args.group, args.port))
+
+ if args.count > 1 and ms:
+ time.sleep(ms)
+
+sock.close()
diff --git a/tests/topotests/pim_basic/r1/bgpd.conf b/tests/topotests/pim_basic/r1/bgpd.conf
new file mode 100644
index 0000000..84d9598
--- /dev/null
+++ b/tests/topotests/pim_basic/r1/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 10.0.30.3 remote-as external
+ neighbor 10.0.30.3 timers 3 10
+ redistribute connected
diff --git a/tests/topotests/pim_basic/r1/pimd.conf b/tests/topotests/pim_basic/r1/pimd.conf
new file mode 100644
index 0000000..737019f
--- /dev/null
+++ b/tests/topotests/pim_basic/r1/pimd.conf
@@ -0,0 +1,18 @@
+hostname r1
+!
+interface r1-eth0
+ ip igmp
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip igmp
+ ip pim
+!
+interface lo
+ ip pim
+!
+ip pim rp 10.254.0.3
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_basic/r1/rp-info.json b/tests/topotests/pim_basic/r1/rp-info.json
new file mode 100644
index 0000000..1f713c2
--- /dev/null
+++ b/tests/topotests/pim_basic/r1/rp-info.json
@@ -0,0 +1,9 @@
+{
+ "10.254.0.3":[
+ {
+ "outboundInterface":"r1-eth1",
+ "group":"224.0.0.0\/4",
+ "source":"Static"
+ }
+ ]
+}
diff --git a/tests/topotests/pim_basic/r1/zebra.conf b/tests/topotests/pim_basic/r1/zebra.conf
new file mode 100644
index 0000000..e430417
--- /dev/null
+++ b/tests/topotests/pim_basic/r1/zebra.conf
@@ -0,0 +1,14 @@
+hostname r1
+!
+interface r1-eth0
+ ip address 10.0.20.1/24
+!
+interface r1-eth1
+ ip address 10.0.30.1/24
+!
+interface r1-eth2
+ ip address 10.0.40.1/24
+!
+interface lo
+ ip address 10.254.0.1/32
+!
diff --git a/tests/topotests/pim_basic/r2/pimd.conf b/tests/topotests/pim_basic/r2/pimd.conf
new file mode 100644
index 0000000..932cff6
--- /dev/null
+++ b/tests/topotests/pim_basic/r2/pimd.conf
@@ -0,0 +1 @@
+hostname r2
diff --git a/tests/topotests/pim_basic/r2/zebra.conf b/tests/topotests/pim_basic/r2/zebra.conf
new file mode 100644
index 0000000..cb30858
--- /dev/null
+++ b/tests/topotests/pim_basic/r2/zebra.conf
@@ -0,0 +1,8 @@
+hostname r2
+!
+interface r2-eth0
+ ip address 10.0.20.2/24
+!
+interface lo
+ ip address 10.254.0.2/32
+!
diff --git a/tests/topotests/pim_basic/r3/pimd.conf b/tests/topotests/pim_basic/r3/pimd.conf
new file mode 100644
index 0000000..f94ee99
--- /dev/null
+++ b/tests/topotests/pim_basic/r3/pimd.conf
@@ -0,0 +1 @@
+hostname r3
diff --git a/tests/topotests/pim_basic/r3/zebra.conf b/tests/topotests/pim_basic/r3/zebra.conf
new file mode 100644
index 0000000..8e58e8c
--- /dev/null
+++ b/tests/topotests/pim_basic/r3/zebra.conf
@@ -0,0 +1,8 @@
+hostname r3
+!
+interface r3-eth0
+ ip address 10.0.40.4/24
+!
+interface lo
+ ip address 10.254.0.4/32
+!
diff --git a/tests/topotests/pim_basic/rp/bgpd.conf b/tests/topotests/pim_basic/rp/bgpd.conf
new file mode 100644
index 0000000..1bfae60
--- /dev/null
+++ b/tests/topotests/pim_basic/rp/bgpd.conf
@@ -0,0 +1,5 @@
+router bgp 65003
+ no bgp ebgp-requires-policy
+ neighbor 10.0.30.1 remote-as external
+ neighbor 10.0.30.1 timers 3 10
+ redistribute connected
diff --git a/tests/topotests/pim_basic/rp/pimd.conf b/tests/topotests/pim_basic/rp/pimd.conf
new file mode 100644
index 0000000..fd26bc4
--- /dev/null
+++ b/tests/topotests/pim_basic/rp/pimd.conf
@@ -0,0 +1,13 @@
+hostname rp
+!
+interface rp-eth0
+ ip pim
+!
+interface lo
+ ip pim
+!
+ip pim join-prune-interval 5
+ip pim rp 10.254.0.3
+ip pim register-accept-list ACCEPT
+
+ip prefix-list ACCEPT seq 5 permit 10.0.20.0/24 le 32
diff --git a/tests/topotests/pim_basic/rp/upstream.json b/tests/topotests/pim_basic/rp/upstream.json
new file mode 100644
index 0000000..c33dea4
--- /dev/null
+++ b/tests/topotests/pim_basic/rp/upstream.json
@@ -0,0 +1,17 @@
+{
+ "229.1.1.1":{
+ "10.0.20.2":{
+ "sourceStream":true,
+ "inboundInterface":"rp-eth0",
+ "rpfAddress":"10.0.20.2",
+ "source":"10.0.20.2",
+ "group":"229.1.1.1",
+ "state":"NotJ",
+ "joinState":"NotJoined",
+ "regState":"RegNoInfo",
+ "resetTimer":"--:--:--",
+ "refCount":1,
+ "sptBit":0
+ }
+ }
+}
diff --git a/tests/topotests/pim_basic/rp/zebra.conf b/tests/topotests/pim_basic/rp/zebra.conf
new file mode 100644
index 0000000..0a1359e
--- /dev/null
+++ b/tests/topotests/pim_basic/rp/zebra.conf
@@ -0,0 +1,8 @@
+hostname rp
+!
+interface rp-eth0
+ ip address 10.0.30.3/24
+!
+interface lo
+ ip address 10.254.0.3/32
+!
diff --git a/tests/topotests/pim_basic/test_pim.py b/tests/topotests/pim_basic/test_pim.py
new file mode 100644
index 0000000..6cea521
--- /dev/null
+++ b/tests/topotests/pim_basic/test_pim.py
@@ -0,0 +1,248 @@
+#!/usr/bin/env python
+
+#
+# test_pim.py
+#
+# Copyright (c) 2018 Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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 Cumulus Networks 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_pim.py: Test pim
+"""
+
+import os
+import sys
+import pytest
+import json
+from functools import partial
+
+pytestmark = pytest.mark.pimd
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.pimd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ tgen.add_router("rp")
+
+ # rp ------ r1 -------- r2
+ # \
+ # --------- r3
+ # r1 -> .1
+ # r2 -> .2
+ # rp -> .3
+ # r3 -> .4
+ # loopback network is 10.254.0.X/32
+ #
+ # r1 <- sw1 -> r2
+ # r1-eth0 <-> r2-eth0
+ # 10.0.20.0/24
+ sw = tgen.add_switch("sw1")
+ sw.add_link(tgen.gears["r1"])
+ sw.add_link(tgen.gears["r2"])
+
+ # r1 <- sw2 -> rp
+ # r1-eth1 <-> rp-eth0
+ # 10.0.30.0/24
+ sw = tgen.add_switch("sw2")
+ sw.add_link(tgen.gears["r1"])
+ sw.add_link(tgen.gears["rp"])
+
+ # 10.0.40.0/24
+ sw = tgen.add_switch("sw3")
+ sw.add_link(tgen.gears["r1"])
+ sw.add_link(tgen.gears["r3"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in tgen.routers().items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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 test_pim_rp_setup():
+ "Ensure basic routing has come up and the rp has an outgoing interface"
+ # Ensure rp and r1 establish pim neighbor ship and bgp has come up
+ # Finally ensure that the rp has an outgoing interface on r1
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ json_file = "{}/{}/rp-info.json".format(CWD, r1.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip pim rp-info json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=15, wait=5)
+ assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_pim_send_mcast_stream():
+ "Establish a Multicast stream from r2 -> r1 and then ensure S,G is created as appropriate"
+ logger.info("Establish a Mcast stream from r2->r1 and then ensure S,G created")
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ rp = tgen.gears["rp"]
+ r3 = tgen.gears["r3"]
+ r2 = tgen.gears["r2"]
+ r1 = tgen.gears["r1"]
+
+ # Let's establish a S,G stream from r2 -> r1
+ CWD = os.path.dirname(os.path.realpath(__file__))
+ r2.run(
+ "{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r2-eth0 > /tmp/bar".format(
+ CWD
+ )
+ )
+ # And from r3 -> r1
+ r3.run(
+ "{}/mcast-tx.py --ttl 5 --count 5 --interval 10 229.1.1.1 r3-eth0 > /tmp/bar".format(
+ CWD
+ )
+ )
+
+ # Let's see that it shows up and we have established some basic state
+ out = r1.vtysh_cmd("show ip pim upstream json", isjson=True)
+ expected = {
+ "229.1.1.1": {
+ "10.0.20.2": {
+ "firstHopRouter": 1,
+ "joinState": "NotJoined",
+ "regState": "RegPrune",
+ "inboundInterface": "r1-eth0",
+ }
+ }
+ }
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip pim upstream json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "failed to converge pim"
+ # tgen.mininet_cli()
+
+
+def test_pim_rp_sees_stream():
+ "Ensure that the RP sees the stream and has acted accordingly"
+ tgen = get_topogen()
+
+ rp = tgen.gears["rp"]
+ json_file = "{}/{}/upstream.json".format(CWD, rp.name)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, rp, "show ip pim upstream json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(rp.name)
+ assert result is None, assertmsg
+
+
+def test_pim_igmp_report():
+ "Send a igmp report from r2->r1 and ensure that the *,G state is created on r1"
+ logger.info("Send a igmp report from r2-r1 and ensure *,G created")
+
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ r2 = tgen.gears["r2"]
+ r1 = tgen.gears["r1"]
+
+ # Let's send a igmp report from r2->r1
+ cmd = [os.path.join(CWD, "mcast-rx.py"), "229.1.1.2", "r2-eth0"]
+ p = r2.popen(cmd)
+ try:
+ expected = {
+ "229.1.1.2": {
+ "*": {
+ "sourceIgmp": 1,
+ "joinState": "Joined",
+ "regState": "RegNoInfo",
+ "sptBit": 0,
+ }
+ }
+ }
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip pim upstream json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=5, wait=0.5)
+ assertmsg = '"{}" JSON output mismatches'.format(r1.name)
+ assert result is None, assertmsg
+ finally:
+ if p:
+ p.terminate()
+ p.wait()
+
+
+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/pim_basic_topo2/__init__.py b/tests/topotests/pim_basic_topo2/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/__init__.py
diff --git a/tests/topotests/pim_basic_topo2/r1/bfdd.conf b/tests/topotests/pim_basic_topo2/r1/bfdd.conf
new file mode 100644
index 0000000..76c6f82
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r1/bfdd.conf
@@ -0,0 +1,6 @@
+bfd
+ profile fast-tx
+ receive-interval 250
+ transmit-interval 250
+ !
+!
diff --git a/tests/topotests/pim_basic_topo2/r1/pimd.conf b/tests/topotests/pim_basic_topo2/r1/pimd.conf
new file mode 100644
index 0000000..b895d7d
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r1/pimd.conf
@@ -0,0 +1,4 @@
+interface r1-eth0
+ ip pim
+ ip pim bfd profile fast-tx
+!
diff --git a/tests/topotests/pim_basic_topo2/r1/zebra.conf b/tests/topotests/pim_basic_topo2/r1/zebra.conf
new file mode 100644
index 0000000..6bf02a3
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r1/zebra.conf
@@ -0,0 +1,3 @@
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/pim_basic_topo2/r2/bfdd.conf b/tests/topotests/pim_basic_topo2/r2/bfdd.conf
new file mode 100644
index 0000000..ca61e46
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r2/bfdd.conf
@@ -0,0 +1,2 @@
+bfd
+!
diff --git a/tests/topotests/pim_basic_topo2/r2/pimd.conf b/tests/topotests/pim_basic_topo2/r2/pimd.conf
new file mode 100644
index 0000000..9f389de
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r2/pimd.conf
@@ -0,0 +1,13 @@
+interface r2-eth0
+ ip pim
+ ip pim bfd
+!
+interface r2-eth1
+ ip pim
+ ip pim bfd
+!
+interface r2-eth2
+ ip pim
+ ip pim bfd
+!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_basic_topo2/r2/zebra.conf b/tests/topotests/pim_basic_topo2/r2/zebra.conf
new file mode 100644
index 0000000..3ceb5f0
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r2/zebra.conf
@@ -0,0 +1,9 @@
+interface r2-eth0
+ ip address 192.168.1.2/24
+!
+interface r2-eth1
+ ip address 192.168.2.1/24
+!
+interface r2-eth2
+ ip address 192.168.3.1/24
+!
diff --git a/tests/topotests/pim_basic_topo2/r3/bfdd.conf b/tests/topotests/pim_basic_topo2/r3/bfdd.conf
new file mode 100644
index 0000000..ca61e46
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r3/bfdd.conf
@@ -0,0 +1,2 @@
+bfd
+!
diff --git a/tests/topotests/pim_basic_topo2/r3/pimd.conf b/tests/topotests/pim_basic_topo2/r3/pimd.conf
new file mode 100644
index 0000000..691a28e
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r3/pimd.conf
@@ -0,0 +1,4 @@
+interface r3-eth0
+ ip pim
+ ip pim bfd
+!
diff --git a/tests/topotests/pim_basic_topo2/r3/zebra.conf b/tests/topotests/pim_basic_topo2/r3/zebra.conf
new file mode 100644
index 0000000..3df218e
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r3/zebra.conf
@@ -0,0 +1,3 @@
+interface r3-eth0
+ ip address 192.168.2.3/24
+!
diff --git a/tests/topotests/pim_basic_topo2/r4/bfdd.conf b/tests/topotests/pim_basic_topo2/r4/bfdd.conf
new file mode 100644
index 0000000..ca61e46
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r4/bfdd.conf
@@ -0,0 +1,2 @@
+bfd
+!
diff --git a/tests/topotests/pim_basic_topo2/r4/pimd.conf b/tests/topotests/pim_basic_topo2/r4/pimd.conf
new file mode 100644
index 0000000..2277b3e
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r4/pimd.conf
@@ -0,0 +1,4 @@
+interface r4-eth0
+ ip pim
+ ip pim bfd
+!
diff --git a/tests/topotests/pim_basic_topo2/r4/zebra.conf b/tests/topotests/pim_basic_topo2/r4/zebra.conf
new file mode 100644
index 0000000..6ac5c78
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/r4/zebra.conf
@@ -0,0 +1,3 @@
+interface r4-eth0
+ ip address 192.168.3.4/24
+!
diff --git a/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.dot b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.dot
new file mode 100644
index 0000000..22fce27
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.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",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.2.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+ sw2 [
+ shape=oval,
+ label="sw2\n192.168.3.0/24",
+ fillcolor="#d0e0d0",
+ style=filled,
+ ];
+
+ # Connections
+ r1 -- sw1 [label="eth0\n.1"];
+ r2 -- sw1 [label="eth0\n.2"];
+
+ r2 -- sw2 [label="eth1\n.1"];
+ r3 -- sw2 [label="eth0\n.3"];
+
+ r2 -- sw3 [label="eth1\n.1"];
+ r4 -- sw3 [label="eth2\n.4"];
+}
diff --git a/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.png b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.png
new file mode 100644
index 0000000..39139a3
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.png
Binary files differ
diff --git a/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py
new file mode 100644
index 0000000..9506c3c
--- /dev/null
+++ b/tests/topotests/pim_basic_topo2/test_pim_basic_topo2.py
@@ -0,0 +1,238 @@
+#!/usr/bin/env python
+
+#
+# test_pim_basic_topo2.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2021 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_pim_basic_topo2.py: Test the FRR PIM protocol convergence.
+"""
+
+import os
+import sys
+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.
+
+pytestmark = [pytest.mark.bfdd, pytest.mark.pimd]
+
+
+def build_topo(tgen):
+ "Build function"
+
+ # 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["r2"])
+ switch.add_link(tgen.gears["r4"])
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ daemon_file = "{}/{}/bfdd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_BFD, daemon_file)
+
+ daemon_file = "{}/{}/pimd.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_PIM, daemon_file)
+
+ daemon_file = "{}/{}/zebra.conf".format(CWD, rname)
+ if os.path.isfile(daemon_file):
+ router.load_config(TopoRouter.RD_ZEBRA, daemon_file)
+
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def expect_neighbor(router, interface, peer):
+ "Wait until peer is present on interface."
+ logger.info("waiting peer {} in {}".format(peer, interface))
+ tgen = get_topogen()
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip pim neighbor json",
+ {interface: {peer: {}}},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=130, wait=1)
+ assertmsg = '"{}" PIM convergence failure'.format(router)
+ assert result is None, assertmsg
+
+
+def test_wait_pim_convergence():
+ "Wait for PIM to converge"
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for PIM to converge")
+
+ expect_neighbor("r1", "r1-eth0", "192.168.1.2")
+ expect_neighbor("r2", "r2-eth0", "192.168.1.1")
+
+ expect_neighbor("r2", "r2-eth1", "192.168.2.3")
+ expect_neighbor("r2", "r2-eth2", "192.168.3.4")
+
+ expect_neighbor("r3", "r3-eth0", "192.168.2.1")
+ expect_neighbor("r4", "r4-eth0", "192.168.3.1")
+
+
+def test_bfd_peers():
+ "Wait for BFD peers to show up."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for BFD to converge")
+
+ def expect_bfd_peer(router, peer):
+ "Wait until peer is present on interface."
+ logger.info("waiting BFD peer {} in {}".format(peer, router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show bfd peers json",
+ [{"peer": peer, "status": "up"}],
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=10, wait=1)
+ assertmsg = '"{}" BFD convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_bfd_peer("r1", "192.168.1.2")
+ expect_bfd_peer("r2", "192.168.1.1")
+ expect_bfd_peer("r2", "192.168.2.3")
+ expect_bfd_peer("r2", "192.168.3.4")
+ expect_bfd_peer("r3", "192.168.2.1")
+ expect_bfd_peer("r4", "192.168.3.1")
+
+
+def test_pim_reconvergence():
+ "Disconnect a peer and expect it to disconnect."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("waiting for disconnect convergence")
+ tgen.gears["r4"].link_enable("r4-eth0", enabled=False)
+
+ def expect_neighbor_down(router, interface, peer):
+ "Wait until peer is present on interface."
+ logger.info("waiting peer {} in {} to disappear".format(peer, interface))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show ip pim neighbor json",
+ {interface: {peer: None}},
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=4, wait=1)
+ assertmsg = '"{}" PIM convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_neighbor_down("r2", "r2-eth2", "192.168.3.4")
+
+ logger.info("waiting for reconvergence")
+ tgen.gears["r4"].link_enable("r4-eth0", enabled=True)
+ expect_neighbor("r2", "r2-eth2", "192.168.3.4")
+
+
+def test_pim_bfd_profile():
+ "Test that the BFD profile is properly applied in BFD."
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def expect_bfd_peer_settings(router, settings):
+ "Expect the following BFD configuration"
+ logger.info("Verifying BFD peer {} in {}".format(settings["peer"], router))
+ test_func = partial(
+ topotest.router_json_cmp,
+ tgen.gears[router],
+ "show bfd peers json",
+ [settings],
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=4, wait=1)
+ assertmsg = '"{}" BFD convergence failure'.format(router)
+ assert result is None, assertmsg
+
+ expect_bfd_peer_settings(
+ "r1",
+ {
+ "peer": "192.168.1.2",
+ "receive-interval": 250,
+ "transmit-interval": 250,
+ },
+ )
+
+ expect_bfd_peer_settings(
+ "r2",
+ {
+ "peer": "192.168.1.1",
+ "remote-receive-interval": 250,
+ "remote-transmit-interval": 250,
+ },
+ )
+
+
+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/pim_igmp_vrf/h1/zebra.conf b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
new file mode 100644
index 0000000..3d6540d
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h1/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h1
+log file zebra.log
+!
+interface h1-eth0
+ description connection to r1 via sw1
+ ip address 192.168.100.10/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h2/zebra.conf b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
new file mode 100644
index 0000000..95342f9
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h2/zebra.conf
@@ -0,0 +1,8 @@
+hostname h2
+!
+interface h2-eth0
+ description connection to r1 via sw2
+ ip address 192.168.101.2/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h3/zebra.conf b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
new file mode 100644
index 0000000..ef99b1c
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h3/zebra.conf
@@ -0,0 +1,10 @@
+!
+hostname h3
+log file zebra.log
+!
+interface h3-eth0
+ description connection to r1 via sw3
+ ip address 192.168.100.20/24
+!
+ip route 0.0.0.0/0 192.168.100.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/h4/zebra.conf b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
new file mode 100644
index 0000000..6a2e466
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/h4/zebra.conf
@@ -0,0 +1,8 @@
+hostname h4
+!
+interface h4-eth0
+ description connection to r1 via sw4
+ ip address 192.168.101.4/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
new file mode 100644
index 0000000..1e70fcc
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_blue_neighbor.json
@@ -0,0 +1,15 @@
+{
+ "blue":{
+ "vrfName":"blue",
+ "neighbors":{
+ "192.168.0.11":[
+ {
+ "priority":10,
+ "converged":"Full",
+ "address":"192.168.101.11",
+ "ifaceName":"r1-eth1:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
new file mode 100644
index 0000000..7f2ab24
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospf_red_neighbor.json
@@ -0,0 +1,16 @@
+{
+ "red":{
+ "vrfName":"red",
+ "neighbors":{
+ "192.168.0.12":[
+ {
+ "priority":10,
+ "converged":"Full",
+ "address":"192.168.101.12",
+ "ifaceName":"r1-eth3:192.168.101.1"
+ }
+ ]
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/ospfd.conf b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
new file mode 100644
index 0000000..88eb5a8
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/ospfd.conf
@@ -0,0 +1,26 @@
+hostname r1
+!
+! debug ospf event
+!
+!
+interface r1-eth1
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+interface r1-eth3
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 20
+!
+router ospf vrf blue
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+router ospf vrf red
+ ospf router-id 192.168.0.1
+ network 192.168.0.1/32 area 0
+ network 192.168.100.0/24 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
new file mode 100644
index 0000000..8568bae
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_join.json
@@ -0,0 +1,22 @@
+{
+ "r1-eth0":{
+ "name":"r1-eth0",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
+
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
new file mode 100644
index 0000000..ea7d4ac
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "blue":{
+ },
+ "r1-eth0":{
+ },
+ "r1-eth1":{
+ "192.168.101.11":{
+ "interface":"r1-eth1",
+ "neighbor":"192.168.101.11",
+ "drPriority":1
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
new file mode 100644
index 0000000..d3642f8
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_blue_pimreg11.json
@@ -0,0 +1,14 @@
+{
+ "pimreg11":{
+ "name":"pimreg11",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
new file mode 100644
index 0000000..d0037ca
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_join.json
@@ -0,0 +1,21 @@
+{
+ "r1-eth2":{
+ "name":"r1-eth2",
+ "state":"up",
+ "address":"192.168.100.1",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "upTime":"--:--:--",
+ "expire":"--:--",
+ "prune":"--:--",
+ "channelJoinName":"NOINFO",
+ "protocolIgmp":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
new file mode 100644
index 0000000..e17b408
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_neighbor.json
@@ -0,0 +1,13 @@
+{
+ "r1-eth2":{
+ },
+ "r1-eth3":{
+ "192.168.101.12":{
+ "interface":"r1-eth3",
+ "neighbor":"192.168.101.12",
+ "drPriority":1
+ }
+ },
+ "red":{
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
new file mode 100644
index 0000000..45b6cd9
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pim_red_pimreg12.json
@@ -0,0 +1,14 @@
+{
+ "pimreg12":{
+ "name":"pimreg12",
+ "state":"up",
+ "address":"0.0.0.0",
+ "flagAllMulticast":true,
+ "lanDelayEnabled":true,
+ "drAddress":"*",
+ "drPriority":1,
+ "drUptime":"--:--:--",
+ "drElections":0,
+ "drChanges":0
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r1/pimd.conf b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
new file mode 100644
index 0000000..040c3d0
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/pimd.conf
@@ -0,0 +1,27 @@
+hostname r1
+!
+! debug igmp events
+! debug igmp packets
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+interface r1-eth0
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth1
+ ip pim
+!
+interface r1-eth2
+ ip igmp
+ ip igmp version 2
+ ip pim
+!
+interface r1-eth3
+ ip pim
+!
+ip pim join-prune-interval 5
diff --git a/tests/topotests/pim_igmp_vrf/r1/zebra.conf b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
new file mode 100644
index 0000000..9da9280
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r1/zebra.conf
@@ -0,0 +1,30 @@
+!
+hostname r1
+log file zebra.log
+!
+ip forwarding
+ipv6 forwarding
+!
+interface blue vrf blue
+ ip address 192.168.0.1/32
+!
+interface red vrf red
+ ip address 192.168.0.1/32
+!
+interface r1-eth0 vrf blue
+ description connection to h1 via sw1
+ ip address 192.168.100.1/24
+!
+interface r1-eth1 vrf blue
+ description connection to r11 via sw2
+ ip address 192.168.101.1/24
+!
+interface r1-eth2 vrf red
+ description connection to h1 via sw3
+ ip address 192.168.100.1/24
+!
+interface r1-eth3 vrf red
+ description connection to r12 via sw4
+ ip address 192.168.101.1/24
+!
+
diff --git a/tests/topotests/pim_igmp_vrf/r11/ospfd.conf b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
new file mode 100644
index 0000000..86fb66d
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r11
+!
+! debug ospf event
+!
+interface r11-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.11
+ network 192.168.0.11/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
new file mode 100644
index 0000000..289bf51
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pim_blue_join.json
@@ -0,0 +1,19 @@
+{
+ "r11-eth0":{
+ "name":"r11-eth0",
+ "state":"up",
+ "address":"192.168.101.11",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r11/pimd.conf b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
new file mode 100644
index 0000000..3e409dd
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/pimd.conf
@@ -0,0 +1,17 @@
+hostname r11
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.11 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r11-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r11/zebra.conf b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
new file mode 100644
index 0000000..137706d
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r11/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r11
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.11/32
+!
+interface r11-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.11/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/ospfd.conf b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
new file mode 100644
index 0000000..f0dcabe
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/ospfd.conf
@@ -0,0 +1,14 @@
+hostname r12
+!
+! debug ospf event
+!
+interface r12-eth0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+ ip ospf priority 10
+!
+router ospf
+ ospf router-id 192.168.0.12
+ network 192.168.0.12/32 area 0
+ network 192.168.101.0/24 area 0
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
new file mode 100644
index 0000000..6926246
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pim_red_join.json
@@ -0,0 +1,19 @@
+{
+ "r12-eth0":{
+ "name":"r12-eth0",
+ "state":"up",
+ "address":"192.168.101.12",
+ "flagMulticast":true,
+ "flagBroadcast":true,
+ "lanDelayEnabled":true,
+ "239.100.0.1":{
+ "*":{
+ "source":"*",
+ "group":"239.100.0.1",
+ "prune":"--:--",
+ "channelJoinName":"JOIN",
+ "protocolPim":1
+ }
+ }
+ }
+}
diff --git a/tests/topotests/pim_igmp_vrf/r12/pimd.conf b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
new file mode 100644
index 0000000..2c308f7
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/pimd.conf
@@ -0,0 +1,17 @@
+hostname r12
+!
+! debug pim events
+! debug pim packets
+! debug pim trace
+! debug pim zebra
+! debug pim bsm
+!
+ip pim rp 192.168.0.12 239.100.0.0/28
+ip pim join-prune-interval 5
+!
+interface lo
+ ip pim
+!
+interface r12-eth0
+ ip pim
+!
diff --git a/tests/topotests/pim_igmp_vrf/r12/zebra.conf b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
new file mode 100644
index 0000000..bede104
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/r12/zebra.conf
@@ -0,0 +1,13 @@
+!
+hostname r12
+log file zebra.log
+!
+interface lo
+ ip address 192.168.0.12/32
+!
+interface r12-eth0
+ description connection to r1 via sw1
+ ip address 192.168.101.12/24
+!
+ip route 0.0.0.0/0 192.168.101.1
+!
diff --git a/tests/topotests/pim_igmp_vrf/test_pim_vrf.py b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
new file mode 100755
index 0000000..f845a4a
--- /dev/null
+++ b/tests/topotests/pim_igmp_vrf/test_pim_vrf.py
@@ -0,0 +1,402 @@
+#!/usr/bin/env python
+
+#
+# test_pim_vrf.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_pim_vrf.py: Test PIM with VRFs.
+"""
+
+# XXX clean up in later commit to avoid conflict on rebase
+# pylint: disable=C0413
+
+# Tests PIM with VRF
+#
+# R1 is split into 2 VRF: Blue and Red, the others are normal
+# routers and Hosts
+# There are 2 similar topologies with overlapping IPs in each
+# section.
+#
+# Test steps:
+# - setup_module()
+# Create topology. Hosts are only using zebra/staticd,
+# no PIM, no OSPF (using IGMPv2 for multicast)
+# - test_ospf_convergence()
+# Wait for OSPF convergence in each VRF. OSPF is run on
+# R1, R11 and R12.
+# - test_pim_convergence()
+# Wait for PIM convergence in each VRF. PIM is run on
+# R1, R11 and R12. R11 is the RP for vrf blue, R12 is RP
+# for vrf red.
+# - test_vrf_pimreg_interfaces()
+# Adding PIM RP in VRF information and verify pimreg
+# interfaces in VRF blue and red
+# - test_mcast_vrf_blue()
+# Start multicast stream for group 239.100.0.1 from Host
+# H2 and join from Host H1 on vrf blue
+# Verify PIM JOIN status on R1 and R11
+# Stop multicast after verification
+# - test_mcast_vrf_red()
+# Start multicast stream for group 239.100.0.1 from Host
+# H4 and join from Host H3 on vrf blue
+# Verify PIM JOIN status on R1 and R12
+# Stop multicast after verification
+# - teardown_module(module)
+# shutdown topology
+#
+
+TOPOLOGY = """
+ +----------+
+ | Host H2 |
+ | Source |
+ +----------+
+ .2 |
++---------+ +------------+ | +---------+
+| Host H1 | 192.168.100.0/24 | | .1 | .11 | Host H2 |
+| receive |------------------| VRF Blue |---------+--------| PIM RP |
+|IGMP JOIN| .10 .1 | | 192.168.101.0/24 | |
++---------+ | | +---------+
+ =| = = R1 = = |=
++---------+ | | +---------+
+| Host H3 | 192.168.100.0/24 | | 192.168.101.0/24 | Host H4 |
+| receive |------------------| VRF Red |---------+--------| PIM RP |
+|IGMP JOIN| .20 .1 | | .1 | .12 | |
++---------+ +------------+ | +---------+
+ .4 |
+ +----------+
+ | Host H4 |
+ | Source |
+ +----------+
+"""
+
+import json
+import functools
+import os
+import sys
+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
+from lib.topotest import iproute2_is_vrf_capable
+from lib.common_config import required_linux_kernel_version
+from lib.pim import McastTesterHelper
+
+
+pytestmark = [pytest.mark.ospfd, pytest.mark.pimd]
+
+
+def build_topo(tgen):
+ for hostNum in range(1, 5):
+ tgen.add_router("h{}".format(hostNum))
+
+ # Create the main router
+ tgen.add_router("r1")
+
+ # Create the PIM RP routers
+ for rtrNum in range(11, 13):
+ tgen.add_router("r{}".format(rtrNum))
+
+ # Setup Switches and connections
+ for swNum in range(1, 5):
+ tgen.add_switch("sw{}".format(swNum))
+
+ ################
+ # 1st set of connections to routers for VRF red
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h1"].add_link(tgen.gears["sw1"])
+ tgen.gears["r1"].add_link(tgen.gears["sw1"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw2"])
+ tgen.gears["h2"].add_link(tgen.gears["sw2"])
+ tgen.gears["r11"].add_link(tgen.gears["sw2"])
+
+ ################
+ # 2nd set of connections to routers for vrf blue
+ ################
+
+ # Add connections H1 to R1 switch sw1
+ tgen.gears["h3"].add_link(tgen.gears["sw3"])
+ tgen.gears["r1"].add_link(tgen.gears["sw3"])
+
+ # Add connections R1 to R1x switch sw2
+ tgen.gears["r1"].add_link(tgen.gears["sw4"])
+ tgen.gears["h4"].add_link(tgen.gears["sw4"])
+ tgen.gears["r12"].add_link(tgen.gears["sw4"])
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def setup_module(module):
+ logger.info("PIM IGMP VRF Topology: \n {}".format(TOPOLOGY))
+
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ vrf_setup_cmds = [
+ "ip link add name blue type vrf table 11",
+ "ip link add name red type vrf table 12",
+ "ip link set dev blue up",
+ "ip link set dev red up",
+ "ip link set dev r1-eth0 vrf blue up",
+ "ip link set dev r1-eth1 vrf blue up",
+ "ip link set dev r1-eth2 vrf red up",
+ "ip link set dev r1-eth3 vrf red up",
+ ]
+
+ # Starting Routers
+ router_list = tgen.routers()
+
+ # Create VRF on r2 first and add it's interfaces
+ for cmd in vrf_setup_cmds:
+ tgen.net["r1"].cmd(cmd)
+
+ for rname, router in router_list.items():
+ logger.info("Loading router %s" % rname)
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ if rname[0] != "h":
+ # Only load ospf on routers, not on end hosts
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_PIM, os.path.join(CWD, "{}/pimd.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(module):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_ospf_convergence():
+ "Test for OSPFv2 convergence"
+ tgen = get_topogen()
+
+ # Required linux kernel version for this suite to run.
+ result = required_linux_kernel_version("4.15")
+ if result is not True:
+ pytest.skip("Kernel requirements are not met")
+
+ # iproute2 needs to support VRFs for this suite to run.
+ if not iproute2_is_vrf_capable():
+ pytest.skip("Installed iproute2 version does not support VRFs")
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip ospf vrf blue neighbor json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF blue"
+ assert res is None, assertmsg
+
+ logger.info("Checking OSPFv2 convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/ospf_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip ospf vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=60, wait=2)
+ assertmsg = "OSPF router R1 did not converge on VRF red"
+ assert res is None, assertmsg
+
+
+def test_pim_convergence():
+ "Test for PIM convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ logger.info("Checking PIM convergence on router r1 for VRF red")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_red_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf red neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF red"
+ assert res is None, assertmsg
+
+ logger.info("Checking PIM convergence on router r1 for VRF blue")
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_blue_neighbor.json")
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim vrf blue neighbor json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=30, wait=2)
+ assertmsg = "PIM router R1 did not converge for VRF blue"
+ assert res is None, assertmsg
+
+
+def test_vrf_pimreg_interfaces():
+ "Adding PIM RP in VRF information and verify pimreg interfaces"
+ tgen = get_topogen()
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\ninterface blue\nip pim")
+ r1.vtysh_cmd("conf\nvrf blue\nip pim rp 192.168.0.11 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg11 interface on R1, VRF blue
+ reffile = os.path.join(CWD, "r1/pim_blue_pimreg11.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip pim vrf blue inter pimreg11 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF blue (table 11) pimreg11 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+ r1.vtysh_cmd("conf\ninterface red\nip pim")
+ r1.vtysh_cmd("conf\nvrf red\nip pim rp 192.168.0.12 239.100.0.1/32\nexit-vrf")
+
+ # Check pimreg12 interface on R1, VRF red
+ reffile = os.path.join(CWD, "r1/pim_red_pimreg12.json")
+ expected = json.loads(open(reffile).read())
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip pim vrf red inter pimreg12 json",
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=5, wait=2)
+ assertmsg = "PIM router R1, VRF red (table 12) pimreg12 interface missing or incorrect status"
+ assert res is None, assertmsg
+
+
+##################################
+### Test PIM / IGMP with VRF
+##################################
+
+
+def check_mcast_entry(mcastaddr, pimrp, receiver, sender, vrf):
+ "Helper function to check RP"
+ tgen = get_topogen()
+
+ logger.info("Testing PIM for VRF {} entry using {}".format(vrf, mcastaddr))
+
+ with McastTesterHelper(tgen) as helper:
+ helper.run(sender, ["--send=0.7", mcastaddr, str(sender) + "-eth0"])
+ helper.run(receiver, [mcastaddr, str(receiver) + "-eth0"])
+
+ logger.info("mcast join and source for {} started".format(mcastaddr))
+
+ router = tgen.gears["r1"]
+ reffile = os.path.join(CWD, "r1/pim_{}_join.json".format(vrf))
+ expected = json.loads(open(reffile).read())
+
+ logger.info("verifying pim join on r1 for {} on VRF {}".format(mcastaddr, vrf))
+ test_func = functools.partial(
+ topotest.router_json_cmp,
+ router,
+ "show ip pim vrf {} join json".format(vrf),
+ expected,
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = "PIM router r1 did not show join status on VRF {}".format(vrf)
+ assert res is None, assertmsg
+
+ logger.info("verifying pim join on PIM RP {} for {}".format(pimrp, mcastaddr))
+ router = tgen.gears[pimrp]
+ reffile = os.path.join(CWD, "{}/pim_{}_join.json".format(pimrp, vrf))
+ expected = json.loads(open(reffile).read())
+
+ test_func = functools.partial(
+ topotest.router_json_cmp, router, "show ip pim join json", expected
+ )
+ _, res = topotest.run_and_expect(test_func, None, count=10, wait=2)
+ assertmsg = (
+ "PIM router {} did not get selected as the PIM RP for VRF {}".format(
+ pimrp, vrf
+ )
+ )
+ assert res is None, assertmsg
+
+
+def test_mcast_vrf_blue():
+ "Test vrf blue with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry("239.100.0.1", "r11", "h1", "h2", "blue")
+
+
+def test_mcast_vrf_red():
+ "Test vrf red with 239.100.0.1"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ check_mcast_entry("239.100.0.1", "r12", "h3", "h4", "red")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/pytest.ini b/tests/topotests/pytest.ini
new file mode 100644
index 0000000..7dd1393
--- /dev/null
+++ b/tests/topotests/pytest.ini
@@ -0,0 +1,83 @@
+# Skip pytests example directory
+[pytest]
+
+# We always turn this on inside conftest.py, default shown
+# addopts = --junitxml=<rundir>/topotests.xml
+
+log_level = DEBUG
+log_format = %(asctime)s,%(msecs)03d %(levelname)s: %(name)s: %(message)s
+log_date_format = %Y-%m-%d %H:%M:%S
+
+# If verbose is specifyied log_cli will be set to 1, it can also be specified
+# here or on the CLI.
+# log_cli = 1
+log_cli_level = INFO
+log_cli_format = %(asctime)s,%(msecs)03d %(levelname)s: %(name)s: %(message)s
+log_cli_date_format = %Y-%m-%d %H:%M:%S
+
+# By default this is palced in <rundir>/exec.log
+# log_file = <rundir>/exec.log
+log_file_level = DEBUG
+log_file_format = %(asctime)s,%(msecs)03d %(levelname)s: %(name)s: %(message)s
+log_file_date_format = %Y-%m-%d %H:%M:%S
+
+junit_logging = all
+junit_log_passing_tests = true
+
+norecursedirs = .git example_test example_topojson_test lib docker
+
+# Directory to store test results and run logs in, default shown
+# rundir = /tmp/topotests
+
+# Markers
+#
+# Please consult the documentation and discuss with TSC members before applying
+# any changes to this list.
+markers =
+ babeld: Tests that run against BABELD
+ bfdd: Tests that run against BFDD
+ bgpd: Tests that run against BGPD
+ eigrpd: Tests that run against EIGRPD
+ isisd: Tests that run against ISISD
+ ldpd: Tests that run against LDPD
+ nhrpd: Tests that run against NHRPD
+ ospf6d: Tests that run against OSPF6D
+ ospfd: Tests that run against OSPFD
+ pathd: Tests that run against PATHD
+ pbrd: Tests that run against PBRD
+ pimd: Tests that run against PIMD
+ ripd: Tests that run against RIPD
+ ripngd: Tests that run against RIPNGD
+ sharpd: Tests that run against SHARPD
+ staticd: Tests that run against STATICD
+ vrrpd: Tests that run against VRRPD
+ snmp: Tests that run against snmp changes
+
+[topogen]
+# Default configuration values
+#
+# 'verbosity' controls how much data the underline systems will use to
+# provide output (e.g. mininet output, test debug output etc...). The
+# value is 'info', but can be changed to 'debug' to provide more details.
+#verbosity = info
+
+# Save logs to log file, by default logs will be displayed to console
+#frrtest_log_dir = /tmp/topotests/
+
+# Display router current configuration during test execution,
+# by default configuration will not be shown
+# show_router_config = True
+
+# Default daemons binaries path.
+#frrdir = /usr/lib/frr
+
+# Default router type to use. Possible values are:
+# 'frr'
+#routertype = frr
+
+# Memory leak test reports path
+# Enables and add an output path to memory leak tests.
+# Example:
+# memleak_path = /tmp/memleak_
+# Output files will be named after the testname:
+# /tmp/memleak_test_ospf_topo1.txt
diff --git a/tests/topotests/rip_topo1/r1/rip_status.ref b/tests/topotests/rip_topo1/r1/rip_status.ref
new file mode 100644
index 0000000..31ad46a
--- /dev/null
+++ b/tests/topotests/rip_topo1/r1/rip_status.ref
@@ -0,0 +1,22 @@
+Routing Protocol is "rip"
+ Sending updates every 5 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 2, receive version 2
+ Interface Send Recv Key-chain
+ r1-eth1 2 2
+ r1-eth2 2 2
+ r1-eth3 2 2
+ Routing for Networks:
+ 193.1.1.0/26
+ r1-eth2
+ r1-eth3
+ Passive Interface(s):
+ r1-eth3
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ 193.1.1.2 0 0 120 XX:XX:XX
+ Distance: (default is 120)
diff --git a/tests/topotests/rip_topo1/r1/ripd.conf b/tests/topotests/rip_topo1/r1/ripd.conf
new file mode 100644
index 0000000..54f1774
--- /dev/null
+++ b/tests/topotests/rip_topo1/r1/ripd.conf
@@ -0,0 +1,13 @@
+log file ripd.log
+!
+router rip
+ timers basic 5 180 5
+ version 2
+ network 193.1.1.0/26
+ network r1-eth2
+ network r1-eth3
+ passive-interface r1-eth3
+!
+line vty
+!
+
diff --git a/tests/topotests/rip_topo1/r1/show_ip_rip.ref b/tests/topotests/rip_topo1/r1/show_ip_rip.ref
new file mode 100644
index 0000000..a0b77c8
--- /dev/null
+++ b/tests/topotests/rip_topo1/r1/show_ip_rip.ref
@@ -0,0 +1,12 @@
+Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface
+
+ Network Next Hop Metric From Tag Time
+R(n) 192.168.2.0/24 193.1.1.2 3 193.1.1.2 0 XX:XX
+R(n) 192.168.3.0/24 193.1.1.2 3 193.1.1.2 0 XX:XX
+C(i) 192.168.98.0/24 0.0.0.0 1 self 0
+C(i) 192.168.99.0/24 0.0.0.0 1 self 0
+C(i) 193.1.1.0/26 0.0.0.0 1 self 0
+R(n) 193.1.2.0/24 193.1.1.2 2 193.1.1.2 0 XX:XX
diff --git a/tests/topotests/rip_topo1/r1/show_ip_route.ref b/tests/topotests/rip_topo1/r1/show_ip_route.ref
new file mode 100644
index 0000000..2ff2604
--- /dev/null
+++ b/tests/topotests/rip_topo1/r1/show_ip_route.ref
@@ -0,0 +1,3 @@
+R>* 192.168.2.0/24 [120/3] via 193.1.1.2, r1-eth1, weight 1
+R>* 192.168.3.0/24 [120/3] via 193.1.1.2, r1-eth1, weight 1
+R>* 193.1.2.0/24 [120/2] via 193.1.1.2, r1-eth1, weight 1
diff --git a/tests/topotests/rip_topo1/r1/zebra.conf b/tests/topotests/rip_topo1/r1/zebra.conf
new file mode 100644
index 0000000..7c8f2c5
--- /dev/null
+++ b/tests/topotests/rip_topo1/r1/zebra.conf
@@ -0,0 +1,26 @@
+log file zebra.log
+!
+hostname r1
+!
+interface r1-eth0
+ ip address 192.168.1.1/24
+!
+interface r1-eth2
+ ip address 192.168.99.1/24
+!
+interface r1-eth3
+ ip address 192.168.98.1/24
+!
+
+interface r1-eth1
+ description to sw2 - RIPv2 interface
+ ip address 193.1.1.1/26
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/rip_topo1/r2/rip_status.ref b/tests/topotests/rip_topo1/r2/rip_status.ref
new file mode 100644
index 0000000..99841a6
--- /dev/null
+++ b/tests/topotests/rip_topo1/r2/rip_status.ref
@@ -0,0 +1,19 @@
+Routing Protocol is "rip"
+ Sending updates every 5 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 2, receive version 2
+ Interface Send Recv Key-chain
+ r2-eth0 2 2
+ r2-eth1 2 2
+ Routing for Networks:
+ 193.1.1.0/26
+ 193.1.2.0/24
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ 193.1.1.1 0 0 120 XX:XX:XX
+ 193.1.2.2 0 0 120 XX:XX:XX
+ Distance: (default is 120)
diff --git a/tests/topotests/rip_topo1/r2/ripd.conf b/tests/topotests/rip_topo1/r2/ripd.conf
new file mode 100644
index 0000000..2e94cfa
--- /dev/null
+++ b/tests/topotests/rip_topo1/r2/ripd.conf
@@ -0,0 +1,12 @@
+log file ripd.log
+!
+!
+router rip
+ version 2
+ timers basic 5 180 5
+ network 193.1.1.0/26
+ network 193.1.2.0/24
+!
+line vty
+!
+
diff --git a/tests/topotests/rip_topo1/r2/show_ip_rip.ref b/tests/topotests/rip_topo1/r2/show_ip_rip.ref
new file mode 100644
index 0000000..b61fb45
--- /dev/null
+++ b/tests/topotests/rip_topo1/r2/show_ip_rip.ref
@@ -0,0 +1,12 @@
+Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface
+
+ Network Next Hop Metric From Tag Time
+R(n) 192.168.2.0/24 193.1.2.2 2 193.1.2.2 0 XX:XX
+R(n) 192.168.3.0/24 193.1.2.2 2 193.1.2.2 0 XX:XX
+R(n) 192.168.98.0/24 193.1.1.1 2 193.1.1.1 0 XX:XX
+R(n) 192.168.99.0/24 193.1.1.1 2 193.1.1.1 0 XX:XX
+C(i) 193.1.1.0/26 0.0.0.0 1 self 0
+C(i) 193.1.2.0/24 0.0.0.0 1 self 0
diff --git a/tests/topotests/rip_topo1/r2/show_ip_route.ref b/tests/topotests/rip_topo1/r2/show_ip_route.ref
new file mode 100644
index 0000000..80f51a9
--- /dev/null
+++ b/tests/topotests/rip_topo1/r2/show_ip_route.ref
@@ -0,0 +1,4 @@
+R>* 192.168.2.0/24 [120/2] via 193.1.2.2, r2-eth1, weight 1
+R>* 192.168.3.0/24 [120/2] via 193.1.2.2, r2-eth1, weight 1
+R>* 192.168.98.0/24 [120/2] via 193.1.1.1, r2-eth0, weight 1
+R>* 192.168.99.0/24 [120/2] via 193.1.1.1, r2-eth0, weight 1
diff --git a/tests/topotests/rip_topo1/r2/zebra.conf b/tests/topotests/rip_topo1/r2/zebra.conf
new file mode 100644
index 0000000..c440f3a
--- /dev/null
+++ b/tests/topotests/rip_topo1/r2/zebra.conf
@@ -0,0 +1,21 @@
+log file zebra.log
+!
+hostname r2
+!
+interface r2-eth0
+ description to sw2 - RIPv2 interface
+ ip address 193.1.1.2/26
+ no link-detect
+!
+interface r2-eth1
+ description to sw3 - RIPv1 interface
+ ip address 193.1.2.1/24
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/rip_topo1/r3/rip_status.ref b/tests/topotests/rip_topo1/r3/rip_status.ref
new file mode 100644
index 0000000..040d3c3
--- /dev/null
+++ b/tests/topotests/rip_topo1/r3/rip_status.ref
@@ -0,0 +1,16 @@
+Routing Protocol is "rip"
+ Sending updates every 5 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing: connected static
+ Default version control: send version 2, receive version 2
+ Interface Send Recv Key-chain
+ r3-eth1 2 2
+ Routing for Networks:
+ 193.1.2.0/24
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ 193.1.2.1 0 0 120 XX:XX:XX
+ Distance: (default is 120)
diff --git a/tests/topotests/rip_topo1/r3/ripd.conf b/tests/topotests/rip_topo1/r3/ripd.conf
new file mode 100644
index 0000000..e27e675
--- /dev/null
+++ b/tests/topotests/rip_topo1/r3/ripd.conf
@@ -0,0 +1,13 @@
+log file ripd.log
+!
+!
+router rip
+ version 2
+ timers basic 5 180 5
+ redistribute connected
+ redistribute static
+ network 193.1.2.0/24
+!
+line vty
+!
+
diff --git a/tests/topotests/rip_topo1/r3/show_ip_rip.ref b/tests/topotests/rip_topo1/r3/show_ip_rip.ref
new file mode 100644
index 0000000..1df299b
--- /dev/null
+++ b/tests/topotests/rip_topo1/r3/show_ip_rip.ref
@@ -0,0 +1,12 @@
+Codes: R - RIP, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface
+
+ Network Next Hop Metric From Tag Time
+S(r) 192.168.2.0/24 192.168.3.10 1 self 0
+C(r) 192.168.3.0/24 0.0.0.0 1 self 0
+R(n) 192.168.98.0/24 193.1.2.1 3 193.1.2.1 0 XX:XX
+R(n) 192.168.99.0/24 193.1.2.1 3 193.1.2.1 0 XX:XX
+R(n) 193.1.1.0/26 193.1.2.1 2 193.1.2.1 0 XX:XX
+C(i) 193.1.2.0/24 0.0.0.0 1 self 0
diff --git a/tests/topotests/rip_topo1/r3/show_ip_route.ref b/tests/topotests/rip_topo1/r3/show_ip_route.ref
new file mode 100644
index 0000000..2b739f0
--- /dev/null
+++ b/tests/topotests/rip_topo1/r3/show_ip_route.ref
@@ -0,0 +1,3 @@
+R>* 192.168.98.0/24 [120/3] via 193.1.2.1, r3-eth1, weight 1
+R>* 192.168.99.0/24 [120/3] via 193.1.2.1, r3-eth1, weight 1
+R>* 193.1.1.0/26 [120/2] via 193.1.2.1, r3-eth1, weight 1
diff --git a/tests/topotests/rip_topo1/r3/zebra.conf b/tests/topotests/rip_topo1/r3/zebra.conf
new file mode 100644
index 0000000..7f145b4
--- /dev/null
+++ b/tests/topotests/rip_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface r3-eth0
+ description to sw4 - Stub interface
+ ip address 192.168.3.1/24
+ no link-detect
+!
+interface r3-eth1
+ description to sw3 - RIPv2 interface
+ ip address 193.1.2.2/24
+ no link-detect
+!
+ip route 192.168.2.0/24 192.168.3.10
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/rip_topo1/test_rip_topo1.dot b/tests/topotests/rip_topo1/test_rip_topo1.dot
new file mode 100644
index 0000000..f052b69
--- /dev/null
+++ b/tests/topotests/rip_topo1/test_rip_topo1.dot
@@ -0,0 +1,61 @@
+## GraphViz file for test_rip_topo1
+##
+## 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 test_rip_topo1 {
+ overlap=false;
+ constraint=false;
+
+ // title
+ labelloc="t";
+ label="Test Topologoy RIP Topo1";
+
+ ######################
+ # Routers
+ ######################
+
+ # Main FRR Router with all protocols
+ R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled];
+
+ # RIP Routers
+ R2 [shape=doubleoctagon, label="R2 FRR\nRIP Router", fillcolor="#19e3d9", style=filled];
+ R3 [shape=doubleoctagon, label="R3 FRR\nRIP Router", fillcolor="#19e3d9", style=filled];
+
+ ######################
+ # Network Lists
+ ######################
+
+ SW1_R1_stub [label="SW1\n192.168.1.0/24", fillcolor="#d0e0d0", style=filled];
+
+ # RIP Networks
+ SW2_R1_R2 [label="SW2\nRIPv2\n193.1.1.0/26", fillcolor="#d0e0d0", style=filled];
+ SW3_R2_R3 [label="SW3\nRIPv1\n193.1.2.0/24", fillcolor="#d0e0d0", style=filled];
+ SW4_R3 [label="SW4\n192.168.3.0/24", fillcolor="#d0e0d0", style=filled];
+ Net_R3_remote [label="Static Net\n192.168.2.0/24"];
+
+ ######################
+ # Network Connections
+ ######################
+ R1 -- SW1_R1_stub [label = "eth0\n.1\n::1"];
+
+ # RIP Network
+ R1 -- SW2_R1_R2 [label = "eth1\n.1"];
+ SW2_R1_R2 -- R2 [label = "eth0\n.2"];
+ R2 -- SW3_R2_R3 [label = "eth1\n.1"];
+ SW3_R2_R3 -- R3 [label = "eth1\n.2"];
+ R3 -- SW4_R3 [label = "eth0\n.1"];
+ SW4_R3 -- Net_R3_remote [label = ".10"];
+
+}
diff --git a/tests/topotests/rip_topo1/test_rip_topo1.pdf b/tests/topotests/rip_topo1/test_rip_topo1.pdf
new file mode 100644
index 0000000..c201ac1
--- /dev/null
+++ b/tests/topotests/rip_topo1/test_rip_topo1.pdf
Binary files differ
diff --git a/tests/topotests/rip_topo1/test_rip_topo1.py b/tests/topotests/rip_topo1/test_rip_topo1.py
new file mode 100644
index 0000000..7d59e84
--- /dev/null
+++ b/tests/topotests/rip_topo1/test_rip_topo1.py
@@ -0,0 +1,364 @@
+#!/usr/bin/env python
+
+#
+# test_rip_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_rip_topo1.py: Testing RIPv2
+
+"""
+
+import os
+import re
+import sys
+import pytest
+from time import sleep
+
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+fatal_error = ""
+
+pytestmark = [pytest.mark.ripd]
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ # Setup RIP Routers
+ for i in range(1, 4):
+ tgen.add_router("r%s" % i)
+
+ #
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+ #
+ # Switches for RIP
+
+ # switch 2 switch is for connection to RIP router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # switch 3 is between RIP routers
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"], nodeif="r3-eth1")
+
+ # switch 4 is stub on remote RIP router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-eth0")
+
+ switch = tgen.add_switch("sw5")
+ switch.add_link(tgen.gears["r1"])
+
+ switch = tgen.add_switch("sw6")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ print("\n\n** %s: Setup Topology" % module.__name__)
+ print("******************************************\n")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ net = tgen.net
+
+ # Starting Routers
+ #
+ for i in range(1, 4):
+ net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ripd", "%s/r%s/ripd.conf" % (thisDir, i))
+ tgen.gears["r%s" % i].start()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(module):
+ print("\n\n** %s: Shutdown Topology" % module.__name__)
+ print("******************************************\n")
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_router_running():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check if FRR is running on each Router node")
+ print("******************************************\n")
+
+ # Make sure that all daemons are running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_converge_protocols():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Waiting for protocols convergence")
+ print("******************************************\n")
+
+ # Not really implemented yet - just sleep 11 secs for now
+ sleep(21)
+
+ # Make sure that all daemons are still running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_rip_status():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify RIP Status
+ print("\n\n** Verifing RIP status")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/rip_status.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ip rip status" 2> /dev/null')
+ .rstrip()
+ )
+ # Drop time in next due
+ actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual IP RIP status",
+ title2="expected IP RIP status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write("r%s failed IP RIP status check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IP RIP status failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are still running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_rip_routes():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify RIP Status
+ print("\n\n** Verifing RIP routes")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/show_ip_rip.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = net["r%s" % i].cmd('vtysh -c "show ip rip" 2> /dev/null').rstrip()
+ # Drop Time
+ actual = re.sub(r"[0-9][0-9]:[0-5][0-9]", "XX:XX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW IP RIP",
+ title2="expected SHOW IP RIP",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write("r%s failed SHOW IP RIP check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "SHOW IP RIP failed for router r%s:\n%s" % (i, diff)
+
+ # Make sure that all daemons are still running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_zebra_ipv4_routingTable():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify OSPFv3 Routing Table
+ print("\n\n** Verifing Zebra IPv4 Routing Table")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/show_ip_route.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ip route" 2> /dev/null | grep "^R"')
+ .rstrip()
+ )
+ # Drop timers on end of line
+ actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual Zebra IPv4 routing table",
+ title2="expected Zebra IPv4 routing table",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed Zebra IPv4 Routing Table Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "Zebra IPv4 Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are still running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_shutdown_check_stderr():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ pytest.skip("Skipping test for Stderr output and memory leaks")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifing unexpected STDERR output from daemons")
+ print("******************************************\n")
+
+ net["r1"].stopRouter()
+
+ log = net["r1"].getStdErr("ripd")
+ if log:
+ print("\nRIPd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("zebra")
+ if log:
+ print("\nZebra StdErr Log:\n" + log)
+
+
+if __name__ == "__main__":
+
+ # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/ripng_topo1/r1/ripng_status.ref b/tests/topotests/ripng_topo1/r1/ripng_status.ref
new file mode 100644
index 0000000..d92ae05
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r1/ripng_status.ref
@@ -0,0 +1,20 @@
+Routing Protocol is "RIPng"
+ Sending updates every 1 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 1, receive version 1
+ Interface Send Recv
+ r1-eth1 1 1
+ r1-eth2 1 1
+ r1-eth3 1 1
+ Routing for Networks:
+ fc00:5::/64
+ r1-eth2
+ r1-eth3
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ fe80::XXXX:XXXX:XXXX:XXXX
+ 0 0 120 XX:XX:XX
diff --git a/tests/topotests/ripng_topo1/r1/ripngd.conf b/tests/topotests/ripng_topo1/r1/ripngd.conf
new file mode 100644
index 0000000..f96297b
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r1/ripngd.conf
@@ -0,0 +1,16 @@
+log file ripngd.log
+!
+! debug ripng events
+! debug ripng packet
+! debug ripng zebra
+!
+router ripng
+ timers basic 1 180 5
+ network fc00:5::/64
+ network r1-eth2
+ network r1-eth3
+ passive-interface r1-eth3
+!
+line vty
+!
+
diff --git a/tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref b/tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref
new file mode 100644
index 0000000..30d0f31
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r1/show_ipv6_ripng.ref
@@ -0,0 +1,18 @@
+Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface, (a/S) - aggregated/Suppressed
+
+ Network Next Hop Via Metric Tag Time
+C(i) fc00:5::/64
+ :: self 1 0
+R(n) fc00:6::/62
+ fe80::XXXX:XXXX:XXXX:XXXX r1-eth1 2 0 XX:XX
+R(n) fc00:7::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r1-eth1 3 0 XX:XX
+R(n) fc00:7:1111::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r1-eth1 3 0 XX:XX
+C(i) fc00:98:0:1::/64
+ :: self 1 0
+C(i) fc00:99:0:1::/64
+ :: self 1 0
diff --git a/tests/topotests/ripng_topo1/r1/show_ipv6_route.ref b/tests/topotests/ripng_topo1/r1/show_ipv6_route.ref
new file mode 100644
index 0000000..55fbbc3
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r1/show_ipv6_route.ref
@@ -0,0 +1,3 @@
+R>* fc00:6::/62 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1
+R>* fc00:7::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1
+R>* fc00:7:1111::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r1-eth1, weight 1
diff --git a/tests/topotests/ripng_topo1/r1/zebra.conf b/tests/topotests/ripng_topo1/r1/zebra.conf
new file mode 100644
index 0000000..11c1cdc
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r1/zebra.conf
@@ -0,0 +1,25 @@
+log file zebra.log
+!
+hostname r1
+!
+interface r1-eth0
+ ipv6 address fc00:0:0:1::1/64
+!
+interface r1-eth1
+ description to sw2 - RIPng interface
+ ipv6 address fc00:5::1/64
+ no link-detect
+!
+interface r1-eth2
+ ipv6 address fc00:99:0:1::1/64
+!
+interface r1-eth3
+ ipv6 address fc00:98:0:1::1/64
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/ripng_topo1/r2/ripng_status.ref b/tests/topotests/ripng_topo1/r2/ripng_status.ref
new file mode 100644
index 0000000..de14b12
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r2/ripng_status.ref
@@ -0,0 +1,20 @@
+Routing Protocol is "RIPng"
+ Sending updates every 1 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing:
+ Default version control: send version 1, receive version 1
+ Interface Send Recv
+ r2-eth0 1 1
+ r2-eth1 1 1
+ Routing for Networks:
+ fc00:5::/64
+ fc00:6::/62
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ fe80::XXXX:XXXX:XXXX:XXXX
+ 0 0 120 XX:XX:XX
+ fe80::XXXX:XXXX:XXXX:XXXX
+ 0 0 120 XX:XX:XX
diff --git a/tests/topotests/ripng_topo1/r2/ripngd.conf b/tests/topotests/ripng_topo1/r2/ripngd.conf
new file mode 100644
index 0000000..7a6450e
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r2/ripngd.conf
@@ -0,0 +1,13 @@
+log file ripngd.log
+!
+! debug ripng events
+! debug ripng packet
+! debug ripng zebra
+!
+router ripng
+ timers basic 1 180 5
+ network fc00:5::/64
+ network fc00:6::/62
+!
+line vty
+!
diff --git a/tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref b/tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref
new file mode 100644
index 0000000..fe5bcc8
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r2/show_ipv6_ripng.ref
@@ -0,0 +1,18 @@
+Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface, (a/S) - aggregated/Suppressed
+
+ Network Next Hop Via Metric Tag Time
+C(i) fc00:5::/64
+ :: self 1 0
+C(i) fc00:6::/62
+ :: self 1 0
+R(n) fc00:7::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r2-eth1 2 0 XX:XX
+R(n) fc00:7:1111::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r2-eth1 2 0 XX:XX
+R(n) fc00:98:0:1::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r2-eth0 2 0 XX:XX
+R(n) fc00:99:0:1::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r2-eth0 2 0 XX:XX
diff --git a/tests/topotests/ripng_topo1/r2/show_ipv6_route.ref b/tests/topotests/ripng_topo1/r2/show_ipv6_route.ref
new file mode 100644
index 0000000..72e1f92
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r2/show_ipv6_route.ref
@@ -0,0 +1,4 @@
+R>* fc00:7::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1, weight 1
+R>* fc00:7:1111::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth1, weight 1
+R>* fc00:98:0:1::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth0, weight 1
+R>* fc00:99:0:1::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r2-eth0, weight 1
diff --git a/tests/topotests/ripng_topo1/r2/zebra.conf b/tests/topotests/ripng_topo1/r2/zebra.conf
new file mode 100644
index 0000000..5900631
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r2/zebra.conf
@@ -0,0 +1,21 @@
+log file zebra.log
+!
+hostname r2
+!
+interface r2-eth0
+ description to sw2 - RIPng interface
+ ipv6 address fc00:5::2/64
+ no link-detect
+!
+interface r2-eth1
+ description to sw3 - RIPng interface
+ ipv6 address fc00:6::1/62
+ no link-detect
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
+
diff --git a/tests/topotests/ripng_topo1/r3/ripng_status.ref b/tests/topotests/ripng_topo1/r3/ripng_status.ref
new file mode 100644
index 0000000..bef2361
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r3/ripng_status.ref
@@ -0,0 +1,16 @@
+Routing Protocol is "RIPng"
+ Sending updates every 1 seconds with +/-50%, next due in XX seconds
+ Timeout after 180 seconds, garbage collect after 5 seconds
+ Outgoing update filter list for all interface is not set
+ Incoming update filter list for all interface is not set
+ Default redistribution metric is 1
+ Redistributing: connected static
+ Default version control: send version 1, receive version 1
+ Interface Send Recv
+ r3-eth1 1 1
+ Routing for Networks:
+ fc00:6::/62
+ Routing Information Sources:
+ Gateway BadPackets BadRoutes Distance Last Update
+ fe80::XXXX:XXXX:XXXX:XXXX
+ 0 0 120 XX:XX:XX
diff --git a/tests/topotests/ripng_topo1/r3/ripngd.conf b/tests/topotests/ripng_topo1/r3/ripngd.conf
new file mode 100644
index 0000000..7a07080
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r3/ripngd.conf
@@ -0,0 +1,15 @@
+log file ripngd.log
+!
+! debug ripng events
+! debug ripng packet
+! debug ripng zebra
+!
+router ripng
+ timers basic 1 180 5
+ network fc00:6::/62
+ redistribute connected
+ redistribute static
+!
+line vty
+!
+
diff --git a/tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref b/tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref
new file mode 100644
index 0000000..909ad66
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r3/show_ipv6_ripng.ref
@@ -0,0 +1,18 @@
+Codes: R - RIPng, C - connected, S - Static, O - OSPF, B - BGP
+Sub-codes:
+ (n) - normal, (s) - static, (d) - default, (r) - redistribute,
+ (i) - interface, (a/S) - aggregated/Suppressed
+
+ Network Next Hop Via Metric Tag Time
+R(n) fc00:5::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r3-eth1 2 0 XX:XX
+C(i) fc00:6::/62
+ :: self 1 0
+C(r) fc00:7::/64
+ :: self 1 0
+S(r) fc00:7:1111::/64
+ :: self 1 0
+R(n) fc00:98:0:1::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r3-eth1 3 0 XX:XX
+R(n) fc00:99:0:1::/64
+ fe80::XXXX:XXXX:XXXX:XXXX r3-eth1 3 0 XX:XX
diff --git a/tests/topotests/ripng_topo1/r3/show_ipv6_route.ref b/tests/topotests/ripng_topo1/r3/show_ipv6_route.ref
new file mode 100644
index 0000000..25a7440
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r3/show_ipv6_route.ref
@@ -0,0 +1,3 @@
+R>* fc00:5::/64 [120/2] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1
+R>* fc00:98:0:1::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1
+R>* fc00:99:0:1::/64 [120/3] via fe80::XXXX:XXXX:XXXX:XXXX, r3-eth1, weight 1
diff --git a/tests/topotests/ripng_topo1/r3/zebra.conf b/tests/topotests/ripng_topo1/r3/zebra.conf
new file mode 100644
index 0000000..b43ba69
--- /dev/null
+++ b/tests/topotests/ripng_topo1/r3/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+hostname r3
+!
+interface r3-eth0
+ description to sw2 - Stub interface
+ ipv6 address fc00:7::1/64
+ no link-detect
+!
+interface r3-eth1
+ description to sw3 - RIPng interface
+ ipv6 address fc00:6::2/62
+ no link-detect
+!
+ipv6 route fc00:7:1111::/64 fc00:7::10
+!
+ip forwarding
+ipv6 forwarding
+!
+!
+line vty
+!
diff --git a/tests/topotests/ripng_topo1/test_ripng_topo1.dot b/tests/topotests/ripng_topo1/test_ripng_topo1.dot
new file mode 100644
index 0000000..7d66a2a
--- /dev/null
+++ b/tests/topotests/ripng_topo1/test_ripng_topo1.dot
@@ -0,0 +1,59 @@
+## GraphViz file for test_all_protocol_topo1
+##
+## 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 test_ripng_topo1 {
+
+ // title
+ labelloc="t";
+ label="Test Topologoy RIPng Topo1";
+
+ ######################
+ # Routers
+ ######################
+
+ # Main FRR Router with all protocols
+ R1 [shape=doubleoctagon, label="R1 FRR\nMain Router", fillcolor="#f08080", style=filled];
+
+ # RIPng Routers
+ R2 [shape=doubleoctagon, label="R2 FRR\nRIPng Router", fillcolor="#fcb314", style=filled];
+ R3 [shape=doubleoctagon, label="R3 FRR\nRIPng Router", fillcolor="#fcb314", style=filled];
+
+ ######################
+ # Network Lists
+ ######################
+
+ SW1_R1_stub [label="SW1\nfc00:0:0:1::/64", fillcolor="#d0e0d0", style=filled];
+
+ # RIPng Networks
+ SW2_R1_R2 [label="SW2\nRIPng\nfc00:5:0:0::/64", fillcolor="#d0e0d0", style=filled];
+ SW3_R2_R3 [label="SW3\nRIPng\nfc00:6:0:0::/62", fillcolor="#d0e0d0", style=filled];
+ SW4_R3 [label="SW4\nfc00::7/128", fillcolor="#d0e0d0", style=filled];
+ Net_R3_remote [label="Static Net\nfc00:7:1111::/64"];
+
+ ######################
+ # Network Connections
+ ######################
+ R1 -- SW1_R1_stub [label = "r1-eth0\n::1"];
+
+ # RIPng Network
+ R1 -- SW2_R1_R2 [label = "r1-eth1\n::1"];
+ SW2_R1_R2 -- R2 [label = "r2-eth0\n::2"];
+ R2 -- SW3_R2_R3 [label = "r2-eth1\n::1"];
+ SW3_R2_R3 -- R3 [label = "r3-eth1\n::2"];
+ R3 -- SW4_R3 [label = "r3-eth0\n::1"];
+ SW4_R3 -- Net_R3_remote [label = ":10"];
+
+}
diff --git a/tests/topotests/ripng_topo1/test_ripng_topo1.pdf b/tests/topotests/ripng_topo1/test_ripng_topo1.pdf
new file mode 100644
index 0000000..cb1adde
--- /dev/null
+++ b/tests/topotests/ripng_topo1/test_ripng_topo1.pdf
Binary files differ
diff --git a/tests/topotests/ripng_topo1/test_ripng_topo1.py b/tests/topotests/ripng_topo1/test_ripng_topo1.py
new file mode 100644
index 0000000..563c4f7
--- /dev/null
+++ b/tests/topotests/ripng_topo1/test_ripng_topo1.py
@@ -0,0 +1,406 @@
+#!/usr/bin/env python
+
+#
+# test_ripng_topo1.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2017 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_ripng_topo1.py: Test of RIPng Topology
+
+"""
+
+import os
+import re
+import sys
+import pytest
+from time import sleep
+
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from lib import topotest
+from lib.topogen import Topogen, get_topogen
+
+fatal_error = ""
+
+pytestmark = [pytest.mark.ripd]
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ # Setup RIPng Routers
+ for i in range(1, 4):
+ tgen.add_router("r%s" % i)
+
+ #
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+ #
+ # Switches for RIPng
+ # switch 2 switch is for connection to RIP router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+ # switch 3 is between RIP routers
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"], nodeif="r3-eth1")
+ # switch 4 is stub on remote RIP router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"], nodeif="r3-eth0")
+
+ switch = tgen.add_switch("sw5")
+ switch.add_link(tgen.gears["r1"])
+ switch = tgen.add_switch("sw6")
+ switch.add_link(tgen.gears["r1"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ print("\n\n** %s: Setup Topology" % module.__name__)
+ print("******************************************\n")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ net = tgen.net
+
+ # Starting Routers
+ #
+ for i in range(1, 4):
+ net["r%s" % i].loadConf("zebra", "%s/r%s/zebra.conf" % (thisDir, i))
+ net["r%s" % i].loadConf("ripngd", "%s/r%s/ripngd.conf" % (thisDir, i))
+ tgen.gears["r%s" % i].start()
+
+ # For debugging after starting FRR daemons, uncomment the next line
+ # tgen.mininet_cli()
+
+
+def teardown_module(module):
+ print("\n\n** %s: Shutdown Topology" % module.__name__)
+ print("******************************************\n")
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_router_running():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ print("\n\n** Check if FRR is running on each Router node")
+ print("******************************************\n")
+
+ # Starting Routers
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_converge_protocols():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Waiting for protocols convergence")
+ print("******************************************\n")
+
+ # Not really implemented yet - just sleep 11 secs for now
+ sleep(11)
+
+ # Make sure that all daemons are running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_ripng_status():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify RIP Status
+ print("\n\n** Verifying RIPng status")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/ripng_status.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ipv6 ripng status" 2> /dev/null')
+ .rstrip()
+ )
+ # Mask out Link-Local mac address portion. They are random...
+ actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
+ # Drop time in next due
+ actual = re.sub(r"in [0-9]+ seconds", "in XX seconds", actual)
+ # Drop time in last update
+ actual = re.sub(r" [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", " XX:XX:XX", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual IPv6 RIPng status",
+ title2="expected IPv6 RIPng status",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed IPv6 RIPng status check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "IPv6 RIPng status failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_ripng_routes():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify RIPng Status
+ print("\n\n** Verifying RIPng routes")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/show_ipv6_ripng.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i].cmd('vtysh -c "show ipv6 ripng" 2> /dev/null').rstrip()
+ )
+ # Drop Time
+ actual = re.sub(r" [0-9][0-9]:[0-5][0-9]", " XX:XX", actual)
+ # Mask out Link-Local mac address portion. They are random...
+ actual = re.sub(
+ r" fe80::[0-9a-f: ]+", " fe80::XXXX:XXXX:XXXX:XXXX ", actual
+ )
+ # Remove trailing spaces on all lines
+ actual = "\n".join([line.rstrip() for line in actual.splitlines()])
+
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual SHOW IPv6 RIPng",
+ title2="expected SHOW IPv6 RIPng",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write("r%s failed SHOW IPv6 RIPng check:\n%s\n" % (i, diff))
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert failures == 0, "SHOW IPv6 RIPng failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_zebra_ipv6_routingTable():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ # Verify OSPFv3 Routing Table
+ print("\n\n** Verifying Zebra IPv6 Routing Table")
+ print("******************************************\n")
+ failures = 0
+ for i in range(1, 4):
+ refTableFile = "%s/r%s/show_ipv6_route.ref" % (thisDir, i)
+ if os.path.isfile(refTableFile):
+ # Read expected result from file
+ expected = open(refTableFile).read().rstrip()
+ # Fix newlines (make them all the same)
+ expected = ("\n".join(expected.splitlines()) + "\n").splitlines(1)
+
+ # Actual output from router
+ actual = (
+ net["r%s" % i]
+ .cmd('vtysh -c "show ipv6 route" 2> /dev/null | grep "^R"')
+ .rstrip()
+ )
+ # Mask out Link-Local mac address portion. They are random...
+ actual = re.sub(r" fe80::[0-9a-f:]+", " fe80::XXXX:XXXX:XXXX:XXXX", actual)
+ # Drop timers on end of line
+ actual = re.sub(r", [0-2][0-9]:[0-5][0-9]:[0-5][0-9]", "", actual)
+ # Fix newlines (make them all the same)
+ actual = ("\n".join(actual.splitlines()) + "\n").splitlines(1)
+
+ # Generate Diff
+ diff = topotest.get_textdiff(
+ actual,
+ expected,
+ title1="actual Zebra IPv6 routing table",
+ title2="expected Zebra IPv6 routing table",
+ )
+
+ # Empty string if it matches, otherwise diff contains unified diff
+ if diff:
+ sys.stderr.write(
+ "r%s failed Zebra IPv6 Routing Table Check:\n%s\n" % (i, diff)
+ )
+ failures += 1
+ else:
+ print("r%s ok" % i)
+
+ assert (
+ failures == 0
+ ), "Zebra IPv6 Routing Table verification failed for router r%s:\n%s" % (
+ i,
+ diff,
+ )
+
+ # Make sure that all daemons are running
+ for i in range(1, 4):
+ fatal_error = net["r%s" % i].checkRouterRunning()
+ assert fatal_error == "", fatal_error
+
+
+def test_shutdown_check_stderr():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_STDERR") is None:
+ print(
+ "SKIPPED final check on StdErr output: Disabled (TOPOTESTS_CHECK_STDERR undefined)\n"
+ )
+ pytest.skip("Skipping test for Stderr output")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ print("\n\n** Verifying unexpected STDERR output from daemons")
+ print("******************************************\n")
+
+ net["r1"].stopRouter()
+
+ log = net["r1"].getStdErr("ripngd")
+ if log:
+ print("\nRIPngd StdErr Log:\n" + log)
+ log = net["r1"].getStdErr("zebra")
+ if log:
+ print("\nZebra StdErr Log:\n" + log)
+
+
+def test_shutdown_check_memleak():
+ global fatal_error
+ net = get_topogen().net
+
+ # Skip if previous fatal error condition is raised
+ if fatal_error != "":
+ pytest.skip(fatal_error)
+
+ if os.environ.get("TOPOTESTS_CHECK_MEMLEAK") is None:
+ print(
+ "SKIPPED final check on Memory leaks: Disabled (TOPOTESTS_CHECK_MEMLEAK undefined)\n"
+ )
+ pytest.skip("Skipping test for memory leaks")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ net["r1"].stopRouter()
+ net["r1"].report_memory_leaks(
+ os.environ.get("TOPOTESTS_CHECK_MEMLEAK"), os.path.basename(__file__)
+ )
+
+
+if __name__ == "__main__":
+
+ # To suppress tracebacks, either use the following pytest call or add "--tb=no" to cli
+ # retval = pytest.main(["-s", "--tb=no"])
+ retval = pytest.main(["-s"])
+ sys.exit(retval)
diff --git a/tests/topotests/route_scale/r1/installed.routes.json b/tests/topotests/route_scale/r1/installed.routes.json
new file mode 100644
index 0000000..25d209f
--- /dev/null
+++ b/tests/topotests/route_scale/r1/installed.routes.json
@@ -0,0 +1,16 @@
+{
+ "routes":[
+ {
+ "fib":32,
+ "rib":32,
+ "type":"connected"
+ },
+ {
+ "fib":1000000,
+ "rib":1000000,
+ "type":"sharp"
+ }
+ ],
+ "routesTotal":1000032,
+ "routesTotalFib":1000032
+}
diff --git a/tests/topotests/route_scale/r1/no.routes.json b/tests/topotests/route_scale/r1/no.routes.json
new file mode 100644
index 0000000..abebd1b
--- /dev/null
+++ b/tests/topotests/route_scale/r1/no.routes.json
@@ -0,0 +1,11 @@
+{
+ "routes":[
+ {
+ "fib":32,
+ "rib":32,
+ "type":"connected"
+ }
+ ],
+ "routesTotal":32,
+ "routesTotalFib":32
+}
diff --git a/tests/topotests/route_scale/r1/sharpd.conf b/tests/topotests/route_scale/r1/sharpd.conf
new file mode 100644
index 0000000..0848f34
--- /dev/null
+++ b/tests/topotests/route_scale/r1/sharpd.conf
@@ -0,0 +1,76 @@
+!
+nexthop-group one
+ nexthop 192.168.0.1 r1-eth0
+!
+nexthop-group two
+ nexthop 192.168.0.1 r1-eth0
+ nexthop 192.168.1.1 r1-eth1
+!
+nexthop-group four
+ nexthop 192.168.0.1 r1-eth0
+ nexthop 192.168.1.1 r1-eth1
+ nexthop 192.168.2.1 r1-eth2
+ nexthop 192.168.3.1 r1-eth3
+!
+nexthop-group eight
+ nexthop 192.168.0.1 r1-eth0
+ nexthop 192.168.1.1 r1-eth1
+ nexthop 192.168.2.1 r1-eth2
+ nexthop 192.168.3.1 r1-eth3
+ nexthop 192.168.4.1 r1-eth4
+ nexthop 192.168.5.1 r1-eth5
+ nexthop 192.168.6.1 r1-eth6
+ nexthop 192.168.7.1 r1-eth7
+!
+nexthop-group sixteen
+ nexthop 192.168.0.1 r1-eth0
+ nexthop 192.168.1.1 r1-eth1
+ nexthop 192.168.2.1 r1-eth2
+ nexthop 192.168.3.1 r1-eth3
+ nexthop 192.168.4.1 r1-eth4
+ nexthop 192.168.5.1 r1-eth5
+ nexthop 192.168.6.1 r1-eth6
+ nexthop 192.168.7.1 r1-eth7
+ nexthop 192.168.8.1 r1-eth8
+ nexthop 192.168.9.1 r1-eth9
+ nexthop 192.168.10.1 r1-eth10
+ nexthop 192.168.11.1 r1-eth11
+ nexthop 192.168.12.1 r1-eth12
+ nexthop 192.168.13.1 r1-eth13
+ nexthop 192.168.14.1 r1-eth14
+ nexthop 192.168.15.1 r1-eth15
+!
+nexthop-group thirtytwo
+ nexthop 192.168.0.1 r1-eth0
+ nexthop 192.168.1.1 r1-eth1
+ nexthop 192.168.2.1 r1-eth2
+ nexthop 192.168.3.1 r1-eth3
+ nexthop 192.168.4.1 r1-eth4
+ nexthop 192.168.5.1 r1-eth5
+ nexthop 192.168.6.1 r1-eth6
+ nexthop 192.168.7.1 r1-eth7
+ nexthop 192.168.8.1 r1-eth8
+ nexthop 192.168.9.1 r1-eth9
+ nexthop 192.168.10.1 r1-eth10
+ nexthop 192.168.11.1 r1-eth11
+ nexthop 192.168.12.1 r1-eth12
+ nexthop 192.168.13.1 r1-eth13
+ nexthop 192.168.14.1 r1-eth14
+ nexthop 192.168.15.1 r1-eth15
+ nexthop 192.168.16.1 r1-eth16
+ nexthop 192.168.17.1 r1-eth17
+ nexthop 192.168.18.1 r1-eth18
+ nexthop 192.168.19.1 r1-eth19
+ nexthop 192.168.20.1 r1-eth20
+ nexthop 192.168.21.1 r1-eth21
+ nexthop 192.168.22.1 r1-eth22
+ nexthop 192.168.23.1 r1-eth23
+ nexthop 192.168.24.1 r1-eth24
+ nexthop 192.168.25.1 r1-eth25
+ nexthop 192.168.26.1 r1-eth26
+ nexthop 192.168.27.1 r1-eth27
+ nexthop 192.168.28.1 r1-eth28
+ nexthop 192.168.29.1 r1-eth29
+ nexthop 192.168.30.1 r1-eth30
+ nexthop 192.168.31.1 r1-eth31
+! \ No newline at end of file
diff --git a/tests/topotests/route_scale/r1/zebra.conf b/tests/topotests/route_scale/r1/zebra.conf
new file mode 100644
index 0000000..48a01f4
--- /dev/null
+++ b/tests/topotests/route_scale/r1/zebra.conf
@@ -0,0 +1,96 @@
+int r1-eth0
+ ip addr 192.168.0.1/24
+!
+int r1-eth1
+ ip addr 192.168.1.1/24
+!
+int r1-eth2
+ ip addr 192.168.2.1/24
+!
+int r1-eth3
+ ip addr 192.168.3.1/24
+!
+int r1-eth4
+ ip addr 192.168.4.1/24
+!
+int r1-eth5
+ ip addr 192.168.5.1/24
+!
+int r1-eth6
+ ip addr 192.168.6.1/24
+!
+int r1-eth7
+ ip addr 192.168.7.1/24
+!
+int r1-eth8
+ ip addr 192.168.8.1/24
+!
+int r1-eth9
+ ip addr 192.168.9.1/24
+!
+int r1-eth10
+ ip addr 192.168.10.1/24
+!
+int r1-eth11
+ ip addr 192.168.11.1/24
+!
+int r1-eth12
+ ip addr 192.168.12.1/24
+!
+int r1-eth13
+ ip addr 192.168.13.1/24
+!
+int r1-eth14
+ ip addr 192.168.14.1/24
+!
+int r1-eth15
+ ip addr 192.168.15.1/24
+!
+int r1-eth16
+ ip addr 192.168.16.1/24
+!
+int r1-eth17
+ ip addr 192.168.17.1/24
+!
+int r1-eth18
+ ip addr 192.168.18.1/24
+!
+int r1-eth19
+ ip addr 192.168.19.1/24
+!
+int r1-eth20
+ ip addr 192.168.20.1/24
+!
+int r1-eth21
+ ip addr 192.168.21.1/24
+!
+int r1-eth22
+ ip addr 192.168.22.1/24
+!
+int r1-eth23
+ ip addr 192.168.23.1/24
+!
+int r1-eth24
+ ip addr 192.168.24.1/24
+!
+int r1-eth25
+ ip addr 192.168.25.1/24
+!
+int r1-eth26
+ ip addr 192.168.26.1/24
+!
+int r1-eth27
+ ip addr 192.168.27.1/24
+!
+int r1-eth28
+ ip addr 192.168.28.1/24
+!
+int r1-eth29
+ ip addr 192.168.29.1/24
+!
+int r1-eth30
+ ip addr 192.168.30.1/24
+!
+int r1-eth31
+ ip addr 192.168.31.1/24
+! \ No newline at end of file
diff --git a/tests/topotests/route_scale/scale_test_common.py b/tests/topotests/route_scale/scale_test_common.py
new file mode 100644
index 0000000..856a2d0
--- /dev/null
+++ b/tests/topotests/route_scale/scale_test_common.py
@@ -0,0 +1,215 @@
+#!/usr/bin/env python
+
+#
+# scale_test_common.py
+#
+# Copyright (c) 2020 by
+# Cumulus Networks, Inc.
+# Donald Sharp
+#
+# 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.
+#
+
+"""
+scale_test_common.py: Common routines for testing route scale
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+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
+
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def scale_build_common(tgen):
+ "Build function"
+
+ # Populate routers
+ for routern in range(1, 2):
+ tgen.add_router("r{}".format(routern))
+
+ # Populate switches
+ for switchn in range(1, 33):
+ switch = tgen.add_switch("sw{}".format(switchn))
+ switch.add_link(tgen.gears["r1"])
+
+
+def scale_setup_module(module):
+ "Setup topology"
+ tgen = Topogen(scale_build_common, module.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ 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))
+ )
+
+ tgen.start_router()
+ # tgen.mininet_cli()
+
+
+def scale_teardown_module(_mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+
+ # This function tears down the whole topology.
+ tgen.stop_topology()
+
+
+def scale_converge_protocols():
+ "Wait for protocol convergence"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+
+def run_one_setup(r1, s):
+ "Run one ecmp config"
+
+ # Extract params
+ expected_installed = s["expect_in"]
+ expected_removed = s["expect_rem"]
+
+ retries = s["retries"]
+ wait = s["wait"]
+
+ for d in expected_installed["routes"]:
+ if d["type"] == "sharp":
+ count = d["rib"]
+ break
+
+ logger.info("Testing {} routes X {} ecmp".format(count, s["ecmp"]))
+
+ r1.vtysh_cmd(
+ "sharp install route 1.0.0.0 \
+ nexthop-group {} {}".format(
+ s["nhg"], count
+ ),
+ isjson=False,
+ )
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route summary json", expected_installed
+ )
+ success, result = topotest.run_and_expect(test_func, None, retries, wait)
+ assert success, "Route scale test install failed:\n{}".format(result)
+
+ output = r1.vtysh_cmd("sharp data route", isjson=False)
+ logger.info("{} routes X {} ecmp installed".format(count, s["ecmp"]))
+ logger.info(output)
+ r1.vtysh_cmd("sharp remove route 1.0.0.0 {}".format(count), isjson=False)
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route summary json", expected_removed
+ )
+ success, result = topotest.run_and_expect(test_func, None, retries, wait)
+ assert success, "Route scale test remove failed:\n{}".format(result)
+
+ output = r1.vtysh_cmd("sharp data route", isjson=False)
+ logger.info("{} routes x {} ecmp removed".format(count, s["ecmp"]))
+ logger.info(output)
+
+
+def route_install_helper(iter):
+ "Test route install for a variety of ecmp"
+
+ 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"]
+
+ # Avoid top ecmp case for runs with < 4G memory
+ output = tgen.net.cmd_raises("free")
+ m = re.search(r"Mem:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", output)
+ total_mem = int(m.group(2))
+ if total_mem < 4000000 and iter == 5:
+ logger.info(
+ "Limited memory available: {}, skipping x32 testcase".format(total_mem)
+ )
+ return;
+
+ 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", "retries", "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 = []
+ s = scale_steps[iter]
+
+ d = dict(zip(scale_keys, s))
+ for k in scale_keys:
+ if k not in d:
+ d[k] = scale_defaults[k]
+
+ run_one_setup(r1, d)
+
+
+# Mem leak testcase
+def scale_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()
diff --git a/tests/topotests/route_scale/test_route_scale1.py b/tests/topotests/route_scale/test_route_scale1.py
new file mode 100644
index 0000000..b563883
--- /dev/null
+++ b/tests/topotests/route_scale/test_route_scale1.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+#
+# test_route_scale1.py
+#
+# Copyright (c) 2021 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+# 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_route_scale1.py: Testing route scale
+
+"""
+import os
+import re
+import sys
+import pytest
+import json
+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 scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module
+
+
+pytestmark = [pytest.mark.sharpd]
+
+def build(tgen):
+ scale_build_common(tgen)
+
+def setup_module(module):
+ scale_setup_module(module)
+
+def teardown_module(_mod):
+ scale_teardown_module(_mod)
+
+def test_converge_protocols():
+ scale_converge_protocols()
+
+def test_route_install_2nh():
+ route_install_helper(1)
+
+def test_route_install_4nh():
+ route_install_helper(2)
+
+def test_route_install_16nh():
+ route_install_helper(4)
+
+def test_memory_leak():
+ scale_test_memory_leak()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/route_scale/test_route_scale2.py b/tests/topotests/route_scale/test_route_scale2.py
new file mode 100644
index 0000000..7045995
--- /dev/null
+++ b/tests/topotests/route_scale/test_route_scale2.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+#
+# test_route_scale2.py
+#
+# Copyright (c) 2022 by
+# Nvidia, Inc.
+# Donald Sharp
+#
+# 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 NVIDIA DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NVIDIA 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_route_scale2.py: Testing route scale
+
+"""
+import os
+import re
+import sys
+import pytest
+import json
+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 scale_test_common import scale_build_common, scale_setup_module, route_install_helper, scale_test_memory_leak, scale_converge_protocols, scale_teardown_module
+
+
+pytestmark = [pytest.mark.sharpd]
+
+def build(tgen):
+ scale_build_common(tgen)
+
+def setup_module(module):
+ scale_setup_module(module)
+
+def teardown_module(_mod):
+ scale_teardown_module(_mod)
+
+def test_converge_protocols():
+ scale_converge_protocols()
+
+def test_route_install_1nh():
+ route_install_helper(0)
+
+def test_route_install_8nh():
+ route_install_helper(3)
+
+def test_route_install_32nh():
+ route_install_helper(5)
+
+def test_memory_leak():
+ scale_test_memory_leak()
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/simple_snmp_test/r1/bgpd.conf b/tests/topotests/simple_snmp_test/r1/bgpd.conf
new file mode 100644
index 0000000..00d1e17
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/r1/bgpd.conf
@@ -0,0 +1,6 @@
+log file /tmp/bgpd.log debugging
+!
+router bgp 100
+ bgp router-id 1.1.1.1
+
+agentx
diff --git a/tests/topotests/simple_snmp_test/r1/isisd.conf b/tests/topotests/simple_snmp_test/r1/isisd.conf
new file mode 100644
index 0000000..1a148f0
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/r1/isisd.conf
@@ -0,0 +1,46 @@
+log stdout debugging
+!
+! debug isis route-events
+! debug isis events
+!
+interface r1-eth0
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth1
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface r1-eth2
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ no isis hello padding
+ isis hello-interval 1
+ isis hello-multiplier 3
+ isis network point-to-point
+!
+interface lo
+ ip router isis ISIS1
+ ipv6 router isis ISIS1
+ isis circuit-type level-1
+ isis passive
+ no isis hello padding
+!
+router isis ISIS1
+ net 01.1111.0000.0000.0001.00
+ is-type level-1
+ topology ipv6-unicast
+!
+line vty
+!
diff --git a/tests/topotests/simple_snmp_test/r1/snmpd.conf b/tests/topotests/simple_snmp_test/r1/snmpd.conf
new file mode 100644
index 0000000..740574c
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/r1/snmpd.conf
@@ -0,0 +1,18 @@
+agentAddress udp:1.1.1.1:161
+
+com2sec public 1.1.1.1 public
+
+group public_group v1 public
+group public_group v2c public
+
+access public_group "" any noauth prefix all all none
+
+view all included .1
+
+iquerySecName frr
+rouser frr
+
+master agentx
+
+agentXSocket /etc/frr/agentx
+agentXPerms 777 755 root frr
diff --git a/tests/topotests/simple_snmp_test/r1/zebra.conf b/tests/topotests/simple_snmp_test/r1/zebra.conf
new file mode 100644
index 0000000..5281d00
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/r1/zebra.conf
@@ -0,0 +1,22 @@
+log file zebra.log
+!
+interface r1-eth0
+ ip address 192.168.12.12/24
+ ipv6 address 2000:1:1:12::12/64
+!
+interface r1-eth1
+ ip address 192.168.13.13/24
+ ipv6 address 2000:1:1:13::13/64
+!
+interface r1-eth2
+ ip address 192.168.14.14/24
+ ipv6 address 2000:1:1:14::14/64
+!
+!
+interface lo
+ ip address 1.1.1.1/32
+ ipv6 address 2000:1:1:1::1/128
+!
+!
+!
+line vty
diff --git a/tests/topotests/simple_snmp_test/test_simple_snmp.py b/tests/topotests/simple_snmp_test/test_simple_snmp.py
new file mode 100755
index 0000000..1ca06c6
--- /dev/null
+++ b/tests/topotests/simple_snmp_test/test_simple_snmp.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+
+#
+# test_simple_snmp.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by Volta Networks
+#
+# Permission to use, copy, modify, and/or distribute this software
+# for any purpose with or without fee is hereby granted, provided
+# that the above copyright notice and this permission notice appear
+# in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND NETDEF DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NETDEF BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY
+# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
+# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+# OF THIS SOFTWARE.
+#
+
+"""
+test_bgp_simple snmp.py: Test snmp infrastructure.
+"""
+
+import os
+import sys
+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.topogen import Topogen, TopoRouter, get_topogen
+from lib.snmptest import SnmpTester
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.isisd, pytest.mark.snmp]
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+
+ # skip tests is SNMP not installed
+ if not os.path.isfile("/usr/sbin/snmpd"):
+ error_msg = "SNMP not installed - skipping"
+ pytest.skip(error_msg)
+ # This function initiates the topology build with Topogen...
+ topodef = {"s1": "r1", "s2": "r1", "s3": "r1"}
+ tgen = Topogen(topodef, mod.__name__)
+ # ... and here it calls Mininet initialization functions.
+ tgen.start_topology()
+
+ r1 = tgen.gears["r1"]
+
+ r1.run("ip addr add 192.168.12.12/24 dev r1-eth0")
+ r1.run("ip -6 addr add 2000:1:1:12::12/64 dev r1-eth0")
+ r1.run("ip addr add 192.168.13.13/24 dev r1-eth1")
+ r1.run("ip -6 addr add 2000:1:1:13::13/64 dev r1-eth1")
+ r1.run("ip addr add 192.168.14.14/24 dev r1-eth2")
+ r1.run("ip -6 addr add 2000:1:1:14::14/64 dev r1-eth2")
+ r1.run("ip addr add 1.1.1.1/32 dev lo")
+ r1.run("ip -6 addr add 2000:1:1:1::1/128 dev lo")
+ r1.run("ip addr show")
+
+ router_list = tgen.routers()
+
+ # For all registered routers, load the zebra configuration file
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_ISIS, os.path.join(CWD, "{}/isisd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP,
+ os.path.join(CWD, "{}/bgpd.conf".format(rname)),
+ "-M snmp",
+ )
+ router.load_config(
+ TopoRouter.RD_SNMP,
+ os.path.join(CWD, "{}/snmpd.conf".format(rname)),
+ "-Le -Ivacm_conf,usmConf,iquery -V -DAgentX,trap",
+ )
+
+ # After loading the configurations, this function loads configured daemons.
+ 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_r1_bgp_version():
+ "Wait for protocol convergence"
+ tgen = get_topogen()
+
+ # Skip if previous fatal error condition is raised
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # tgen.mininet_cli()
+ r1 = tgen.gears["r1"]
+ r1_snmp = SnmpTester(r1, "1.1.1.1", "public", "2c")
+ assert r1_snmp.test_oid("bgpVersin", None)
+ assert r1_snmp.test_oid("bgpVersion", "10")
+ assert r1_snmp.test_oid_walk("bgpVersion", ["10"])
+ assert r1_snmp.test_oid_walk("bgpVersion", ["10"], ["0"])
+
+
+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/srv6_locator/__init__.py b/tests/topotests/srv6_locator/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/srv6_locator/__init__.py
diff --git a/tests/topotests/srv6_locator/expected_chunks1.json b/tests/topotests/srv6_locator/expected_chunks1.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks1.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator/expected_chunks2.json b/tests/topotests/srv6_locator/expected_chunks2.json
new file mode 100644
index 0000000..8707384
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks2.json
@@ -0,0 +1,8 @@
+[
+ {
+ "name": "loc1",
+ "chunks": [
+ "2001:db8:1:1::/64"
+ ]
+ }
+]
diff --git a/tests/topotests/srv6_locator/expected_chunks3.json b/tests/topotests/srv6_locator/expected_chunks3.json
new file mode 100644
index 0000000..fe51488
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks3.json
@@ -0,0 +1 @@
+[]
diff --git a/tests/topotests/srv6_locator/expected_chunks4.json b/tests/topotests/srv6_locator/expected_chunks4.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks4.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator/expected_chunks5.json b/tests/topotests/srv6_locator/expected_chunks5.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks5.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator/expected_chunks6.json b/tests/topotests/srv6_locator/expected_chunks6.json
new file mode 100644
index 0000000..0d4f101
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_chunks6.json
@@ -0,0 +1,2 @@
+[
+]
diff --git a/tests/topotests/srv6_locator/expected_ipv6_routes.json b/tests/topotests/srv6_locator/expected_ipv6_routes.json
new file mode 100644
index 0000000..fb92f25
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_ipv6_routes.json
@@ -0,0 +1,29 @@
+{
+ "2001:db8:1:1:1::/80":[
+ {
+ "prefix":"2001:db8:1:1:1::/80",
+ "protocol":"static",
+ "selected":true,
+ "installed":true,
+ "nexthops":[{
+ "fib":true,
+ "active":true,
+ "seg6local":{ "action":"End" }
+ }]
+ }
+ ],
+ "2001:db8:2:2:1::/80":[
+ {
+ "prefix":"2001:db8:2:2:1::/80",
+ "protocol":"static",
+ "selected":true,
+ "installed":true,
+ "nexthops":[{
+ "fib":true,
+ "active":true,
+ "seg6local":{ "action":"End" }
+
+ }]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator/expected_locators1.json b/tests/topotests/srv6_locator/expected_locators1.json
new file mode 100644
index 0000000..3953bb0
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators1.json
@@ -0,0 +1,26 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1:1::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2:2::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2:2::/64",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator/expected_locators2.json b/tests/topotests/srv6_locator/expected_locators2.json
new file mode 100644
index 0000000..ce3a404
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators2.json
@@ -0,0 +1,26 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1:1::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1:1::/64",
+ "proto": "sharp"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2:2::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2:2::/64",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator/expected_locators3.json b/tests/topotests/srv6_locator/expected_locators3.json
new file mode 100644
index 0000000..3953bb0
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators3.json
@@ -0,0 +1,26 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1:1::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2:2::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2:2::/64",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/srv6_locator/expected_locators4.json b/tests/topotests/srv6_locator/expected_locators4.json
new file mode 100644
index 0000000..4b0f95f
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators4.json
@@ -0,0 +1,38 @@
+{
+ "locators":[
+ {
+ "name": "loc1",
+ "prefix": "2001:db8:1:1::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:1:1::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2:2::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2:2::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc3",
+ "prefix": "2001:db8:3:3::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:3:3::/64",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/srv6_locator/expected_locators5.json b/tests/topotests/srv6_locator/expected_locators5.json
new file mode 100644
index 0000000..bcffa00
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators5.json
@@ -0,0 +1,27 @@
+{
+ "locators":[
+ {
+ "name": "loc2",
+ "prefix": "2001:db8:2:2::/64",
+ "statusUp": true,
+ "chunks": [
+ {
+ "prefix": "2001:db8:2:2::/64",
+ "proto": "system"
+ }
+ ]
+ },
+ {
+ "name": "loc3",
+ "prefix": "2001:db8:3:3::/64",
+ "statusUp": true,
+ "chunks":[
+ {
+ "prefix": "2001:db8:3:3::/64",
+ "proto": "system"
+ }
+ ]
+ }
+ ]
+}
+
diff --git a/tests/topotests/srv6_locator/expected_locators6.json b/tests/topotests/srv6_locator/expected_locators6.json
new file mode 100644
index 0000000..66d23d5
--- /dev/null
+++ b/tests/topotests/srv6_locator/expected_locators6.json
@@ -0,0 +1,5 @@
+{
+ "locators":[
+ ]
+}
+
diff --git a/tests/topotests/srv6_locator/r1/setup.sh b/tests/topotests/srv6_locator/r1/setup.sh
new file mode 100644
index 0000000..36ed713
--- /dev/null
+++ b/tests/topotests/srv6_locator/r1/setup.sh
@@ -0,0 +1,2 @@
+ip link add dummy0 type dummy
+ip link set dummy0 up
diff --git a/tests/topotests/srv6_locator/r1/sharpd.conf b/tests/topotests/srv6_locator/r1/sharpd.conf
new file mode 100644
index 0000000..d460859
--- /dev/null
+++ b/tests/topotests/srv6_locator/r1/sharpd.conf
@@ -0,0 +1,7 @@
+hostname r1
+!
+log stdout notifications
+log monitor notifications
+log commands
+log file sharpd.log debugging
+!
diff --git a/tests/topotests/srv6_locator/r1/zebra.conf b/tests/topotests/srv6_locator/r1/zebra.conf
new file mode 100644
index 0000000..85001d7
--- /dev/null
+++ b/tests/topotests/srv6_locator/r1/zebra.conf
@@ -0,0 +1,22 @@
+hostname r1
+!
+! debug zebra events
+! debug zebra rib detailed
+!
+log stdout notifications
+log monitor notifications
+log commands
+log file zebra.log debugging
+!
+segment-routing
+ srv6
+ locators
+ locator loc1
+ prefix 2001:db8:1:1::/64
+ !
+ locator loc2
+ prefix 2001:db8:2:2::/64
+ !
+ !
+ !
+!
diff --git a/tests/topotests/srv6_locator/test_srv6_locator.py b/tests/topotests/srv6_locator/test_srv6_locator.py
new file mode 100755
index 0000000..bc5fa40
--- /dev/null
+++ b/tests/topotests/srv6_locator/test_srv6_locator.py
@@ -0,0 +1,164 @@
+#!/usr/bin/env python
+
+#
+# test_srv6_manager.py
+# Part of NetDEF Topology Tests
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+# 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_srv6_manager.py:
+Test for SRv6 manager on zebra
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.sharpd]
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def setup_module(mod):
+ tgen = Topogen({None: "r1"}, mod.__name__)
+ tgen.start_topology()
+ for rname, router in tgen.routers().items():
+ router.run("/bin/bash {}/{}/setup.sh".format(CWD, rname))
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_SHARP, os.path.join(CWD, "{}/sharpd.conf".format(rname))
+ )
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_srv6():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ router = tgen.gears["r1"]
+
+ def _check_srv6_locator(router, expected_locator_file):
+ logger.info("checking zebra locator status")
+ output = json.loads(router.vtysh_cmd("show segment-routing srv6 locator json"))
+ expected = open_json_file("{}/{}".format(CWD, expected_locator_file))
+ return topotest.json_cmp(output, expected)
+
+ def _check_sharpd_chunk(router, expected_chunk_file):
+ logger.info("checking sharpd locator chunk status")
+ output = json.loads(router.vtysh_cmd("show sharp segment-routing srv6 json"))
+ expected = open_json_file("{}/{}".format(CWD, expected_chunk_file))
+ return topotest.json_cmp(output, expected)
+
+ def check_srv6_locator(router, expected_file):
+ func = functools.partial(_check_srv6_locator, router, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=5, wait=0.5)
+ assert result is None, "Failed"
+
+ def check_sharpd_chunk(router, expected_file):
+ func = functools.partial(_check_sharpd_chunk, router, expected_file)
+ success, result = topotest.run_and_expect(func, None, count=5, wait=0.5)
+ assert result is None, "Failed"
+
+ # FOR DEVELOPER:
+ # If you want to stop some specific line and start interactive shell,
+ # please use tgen.mininet_cli() to start it.
+
+ logger.info("Test1 for Locator Configuration")
+ check_srv6_locator(router, "expected_locators1.json")
+ check_sharpd_chunk(router, "expected_chunks1.json")
+
+ logger.info("Test2 get chunk for locator loc1")
+ router.vtysh_cmd("sharp srv6-manager get-locator-chunk loc1")
+ check_srv6_locator(router, "expected_locators2.json")
+ check_sharpd_chunk(router, "expected_chunks2.json")
+
+ logger.info("Test3 release chunk for locator loc1")
+ router.vtysh_cmd("sharp srv6-manager release-locator-chunk loc1")
+ check_srv6_locator(router, "expected_locators3.json")
+ check_sharpd_chunk(router, "expected_chunks3.json")
+
+ logger.info("Test4 additional locator loc3")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ locator loc3
+ prefix 2001:db8:3:3::/64
+ """
+ )
+ check_srv6_locator(router, "expected_locators4.json")
+ check_sharpd_chunk(router, "expected_chunks4.json")
+
+ logger.info("Test5 delete locator and chunk is released automatically")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ srv6
+ locators
+ no locator loc1
+ """
+ )
+ check_srv6_locator(router, "expected_locators5.json")
+ check_sharpd_chunk(router, "expected_chunks5.json")
+
+ logger.info("Test6 delete srv6 all configuration")
+ router.vtysh_cmd(
+ """
+ configure terminal
+ segment-routing
+ no srv6
+ """
+ )
+ check_srv6_locator(router, "expected_locators6.json")
+ check_sharpd_chunk(router, "expected_chunks6.json")
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json
new file mode 100644
index 0000000..7099043
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo1_ebgp.json
@@ -0,0 +1,157 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json
new file mode 100644
index 0000000..91820b0
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo2_ebgp.json
@@ -0,0 +1,363 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json
new file mode 100644
index 0000000..5246a31
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo3_ebgp.json
@@ -0,0 +1,189 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 29,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json b/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json
new file mode 100644
index 0000000..bb72578
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/static_routes_topo4_ebgp.json
@@ -0,0 +1,428 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "vm1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm6": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py
new file mode 100644
index 0000000..abfba02
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo1_ebgp.py
@@ -0,0 +1,1326 @@
+#!/usr/bin/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.
+#
+"""
+
+ -Verify static route ECMP functionality with 2 next hop.
+
+ -Verify static route functionality with 2 next hop and different AD
+ value.
+
+ -Verify RIB status when same route advertise via BGP and static route.
+
+"""
+import sys
+import time
+import os
+import pytest
+import platform
+
+# 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/"))
+
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]}
+NETWORK2 = {"ipv4": "11.0.20.1/32", "ipv6": "2::1/128"}
+
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+
+
+def setup_module(mod):
+ """
+ Sets up the pytest environment.
+
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/static_routes_topo1_ebgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ converged = verify_bgp_convergence(tgen, topo)
+ assert converged is True, "setup_module :Failed \n Error: {}".format(converged)
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+
+ logger.info("Running teardown_module to delete topology: %s", mod)
+
+ 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)
+
+
+def populate_nh():
+ """
+ Populate nexthops.
+ """
+
+ next_hop_ip = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ }
+ return next_hop_ip
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_static_route_2nh_p0_tc_1_ebgp(request):
+ """
+ Verify static route ECMP functionality with 2 next hop.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ next_hop_ip = populate_nh()
+
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on"
+ "R1"
+ )
+ step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using show ip route"
+ " with 2 ECMP next hop "
+ )
+ nh = [next_hop_ip["nh1"][addr_type], next_hop_ip["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed" "Error: Routes is missing in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the static route configured with nexthop N1 from running config")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ nh = next_hop_ip["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed" "Error: Routes is still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from" "running config")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = next_hop_ip["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Only one the nexthops should be active in RIB.")
+
+ nh = next_hop_ip["nh2"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ nh = next_hop_ip["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r3"
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r2"
+ nh = [next_hop_ip["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r2"
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [next_hop_ip["nh1"][addr_type], next_hop_ip["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = next_hop_ip["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("No shut nexthop interface N2")
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [next_hop_ip["nh1"][addr_type], next_hop_ip["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ dut = "r2"
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_2nh_admin_dist_p0_tc_2_ebgp(request):
+ """
+ Verify static route functionality with 2 next hop & different AD value.
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop"
+ "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & "
+ "ip route 10.1.1.1/24 29.1.1.2 20"
+ )
+
+ reset_config_on_routers(tgen)
+ next_hop_ip = populate_nh()
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 2 next hop , lowest AD nexthop is active "
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Explicit route is added in R3 for R2 nexthop rechability")
+ rt3_rtes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": next_hop_ip["nh1"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ {
+ "network": next_hop_ip["nh2"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt3_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the static route configured with nexthop N1 from" "running config")
+ rt1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from" "running config")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = next_hop_ip["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("after shut of nexthop N1 , route become active with nexthop N2")
+
+ nh = next_hop_ip["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [next_hop_ip["nh1"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = next_hop_ip["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [next_hop_ip["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r2"
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": next_hop_ip["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [next_hop_ip["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_same_rte_from_bgp_static_p0_tc5_ebgp(request):
+ """
+ Verify RIB status when same route advertise via BGP and static
+ route
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ NEXT_HOP_IP = populate_nh()
+ step("Configure EBGP IPv4 peering between R2 and R3 router.")
+
+ step(
+ "Configure IPv4 static route (10.1.1.1/24) in R2 with next hop"
+ "N1 (28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present"
+ "on R1"
+ )
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in BGP.")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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 R3 , route receive on R3 BGP table ")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Verify route installed in the RIB and FIB of R3")
+ protocol = "bgp"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step(
+ "Configure 2 links/interfaces between R1 and R3 , keep one"
+ "interface in shut (active) state and other interface in no shut"
+ "(inactive) state"
+ )
+ dut = "r3"
+ intf = topo["routers"]["r3"]["links"]["r1-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "Configure same static route (10.1.1.1/24) in R3 with inactive"
+ "nexthop interface"
+ )
+
+ step(
+ "Configure same static route 10.1.1.1/24) again in R3 with"
+ "active nexthop interface"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3-link0"][
+ addr_type
+ ],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3-link1"][
+ addr_type
+ ],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify when static route configure with inactive nexthop , "
+ "verify BGP received route is active in the RIB and FIB"
+ )
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in BGP RIB".format(
+ tc_name
+ )
+
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ step("Remove the static route on R3 configured with active" "interface")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3-link0"][
+ addr_type
+ ],
+ "delete": True,
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r1"]["links"]["r3-link1"][
+ addr_type
+ ],
+ "delete": True,
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step(
+ "After removing the static route with active nexthop verify "
+ "BGP received route is became active in RIB and FIB"
+ )
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in BGP RIB".format(
+ tc_name
+ )
+
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py
new file mode 100644
index 0000000..f04279a
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo2_ebgp.py
@@ -0,0 +1,1760 @@
+#!/usr/bin/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.
+#
+
+
+"""
+ -Verify static route functionality with 8 next hop different AD value
+ and BGP ECMP
+
+ -Verify 8 static route functionality with 8 next hop different AD
+
+ -Verify static route with 8 next hop with different AD value and 8
+ EBGP neighbors
+
+ -Verify static route with 8 next hop with different AD value and 8
+ IBGP neighbors
+
+ -Delete the static route and verify the RIB and FIB state
+
+ -Verify 8 static route functionality with 8 ECMP next hop
+"""
+import sys
+import time
+import os
+import pytest
+import platform
+import random
+from lib.topotest import version_cmp
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ "11.0.20.6/32",
+ "11.0.20.7/32",
+ "11.0.20.8/32",
+ ],
+ "ipv6": [
+ "2::1/128",
+ "2::2/128",
+ "2::3/128",
+ "2::4/128",
+ "2::5/128",
+ "2::6/128",
+ "2::7/128",
+ "2::8/128",
+ ],
+}
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+PREFIX2 = {"ipv4": "110.0.20.2/32", "ipv6": "20::2/128"}
+NEXT_HOP_IP = []
+topo_diag = """
+ Please view in a fixed-width font such as Courier.
+ +------+ +------+ +------+
+ | +--------------+ +--------------+ |
+ | | | | | |
+ | R1 +---8 links----+ R2 +---8 links----+ R3 |
+ | | | | | |
+ | +--------------+ +--------------+ |
+ +------+ +------+ +------+
+
+"""
+
+
+def setup_module(mod):
+ """
+
+ Set up the pytest environment.
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo2_ebgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Testcases
+#
+#####################################################
+
+
+def test_static_rte_with_8ecmp_nh_p1_tc9_ebgp(request):
+ """
+ Verify 8 static route functionality with 8 ECMP next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interfaces / links between R2 and R3")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure 8 IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ,"
+ "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ,"
+ "Static route next-hop present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh_all[addr_type] = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ dut = "r2"
+ protocol = "static"
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed\nError: Routes are" " missing in RIB".format(
+ tc_name
+ )
+
+ protocol = "static"
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ # Shutdown interface
+ dut = "r2"
+ step(
+ " interface which is about to be shut no shut between r1 and r2 is " "%s",
+ topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"],
+ )
+ intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Random no shut of the nexthop interfaces")
+ # Bringup interface
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After random shut/no shut of nexthop , only that "
+ "nexthop deleted/added from all the routes , other nexthop remain "
+ "unchanged"
+ )
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ dut = "r2"
+ randnum = random.randint(1, 7)
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After delete of random route , that route only got deleted from"
+ " RIB/FIB other route are showing properly"
+ )
+ nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route "
+ "installed in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nhp = 1
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r2", addr_type)
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the BGP neighbor or redistribute static knob , "
+ "verify route got clear from RIB and FIB of R3 routes "
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ebgp(request):
+ """
+ Verify static route functionality with 8 next hop different AD
+ value and BGP ECMP
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2 ,")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step(
+ "Configure IBGP IPv4 peering over loopback interface between"
+ "R2 and R3 router."
+ )
+ step("Configure redistribute static in BGP on R2 router")
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ retry_timeout=6,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ retry_timeout=6,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "bgp"
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n " "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_ebgp_ecmp_p1_tc8_ebgp(request):
+ """
+ Verify static route with 8 next hop with different AD value and 8
+ EBGP neighbors
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step("Configure 8 EBGP IPv4 peering between R2 and R3")
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ dut = "r2"
+ protocol = "static"
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ebgp(request):
+ """
+ Verify 8 static route functionality with 8 next hop different AD'
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2 ")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80"
+ )
+ step(
+ "Configure nexthop AD in such way for static route S1 , N1 is"
+ "preferred and for S2 , N2 is preferred and so on .."
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Verify that highest AD nexthop are inactive")
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ retry_timeout=6,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which"
+ "got removed is not shown in RIB and FIB"
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ }
+ ]
+ }
+ }
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ "lowest AD is missing in RIB".format(tc_name)
+ )
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop "
+ "which got removed is not shown in RIB and FIB"
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Route "
+ " is still present in RIB".format(tc_name)
+ )
+
+ step("Reconfigure the deleted routes and verify they are installed")
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route "
+ " is still present in RIB".format(tc_name)
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step("After reloading, verify that routes are still present in R2.")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ second_rte,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route " " is missing in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_delete_p0_tc11_ebgp(request):
+ """
+ Delete the static route and verify the RIB and FIB state
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2 ")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80"
+ )
+ step(
+ "Configure nexthop AD in such way for static route S1 , N1 is"
+ "preferred and for S2 , N2 is preferred and so on .."
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Verify that highest AD nexthop are inactive")
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after removing the redistribute static from BGP all the"
+ "routes got delete from RIB and FIB of R3 "
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ " After removing all the routes and nexthop from R2 , "
+ " verify R2 RIB and FIB is cleared"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still active in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
new file mode 100644
index 0000000..088ac60
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo3_ebgp.py
@@ -0,0 +1,1363 @@
+#!/usr/bin/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.
+#
+"""
+ -Verify static route ECMP functionality with 8 next hop
+
+ -Verify static route functionality with 8 next hop different AD value
+
+ -Verify static route with tag option
+
+ -Verify BGP did not install the static route when it receive route
+ with local next hop
+
+"""
+import sys
+import time
+import os
+import pytest
+import random
+import platform
+from lib.topotest import version_cmp
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+ create_route_maps,
+ verify_ip_nht,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ "11.0.20.6/32",
+ "11.0.20.7/32",
+ "11.0.20.8/32",
+ ],
+ "ipv6": [
+ "2::1/128",
+ "2::2/128",
+ "2::3/128",
+ "2::4/128",
+ "2::5/128",
+ "2::6/128",
+ "2::7/128",
+ "2::8/128",
+ ],
+}
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+NETWORK2 = {"ipv4": ["11.0.20.1/32"], "ipv6": ["2::1/128"]}
+NEXT_HOP_IP = []
+
+
+def setup_module(mod):
+ """
+
+ Set up the pytest environment.
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo3_ebgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_staticroute_with_ecmp_p0_tc3_ebgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop'
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2), N2(22.1.1.2), N3(23.1.1.2), N4(24.1.1.2),"
+ "N5(25.1.1.2), N6(26.1.1.2), N7(27.1.1.2),N8(28.1.1.2), Static"
+ "route next-hop present on R1"
+ )
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ebgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop, lowest AD nexthop is active"
+ )
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring them, route is always active with lowest AD"
+ "value and all the nexthop populated in RIB and FIB again "
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one, "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router, static route installed "
+ "in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_local_nexthop_p1_tc14_ebgp(request):
+ """
+ Verify BGP did not install the static route when it receive route
+ with local next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ step("Configure BGP IPv4 session between R2 and R3")
+ step("Configure IPv4 static route on R2")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in the BGP")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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 R2 BGP table has IPv4 route")
+ dut = "r2"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB of R2".format(
+ tc_name
+ )
+
+ step(" Verify route did not install in the R3 BGP table, RIB/FIB")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes is"
+ " still present in BGP RIB of R2".format(tc_name)
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB of R2".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_frr_intf_name_as_gw_gap_tc4_ebgp_p0(request):
+ """
+ Verify static route configure with interface name as gateway'
+ 'address'
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ dut = "r1"
+ intf = topo["routers"]["r1"]["links"]["r2-link0"]["interface"]
+ nh = topo["routers"]["r1"]["links"]["r2-link0"]
+ ip_list = {
+ "ipv4": [(dut, intf, ["1.1.1.1/32"], nh["ipv4"].split("/")[0])],
+ "ipv6": [(dut, intf, ["4001::32/128"], nh["ipv6"].split("/")[0])],
+ }
+
+ step(
+ "Configure IPv4 and IPv6 static route in FRR with different next"
+ "hop (ens224 as nexthop))"
+ )
+ step("ip route 2.2.2.0/24 20.1.1.1 ens224 ----from FRR cli")
+ step("ipv6 route 2000::1/120 5000::1 ens224 ----from FRR cli")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {"network": ip_list[addr_type][0][2][0], "next_hop": nh}
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "IPv4 and IPv6 Static route added in FRR verify using "
+ "show ip route , nexthop is resolved using show nht"
+ )
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, next_hop=nh
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ input_dict_nh = {
+ "r1": {
+ nh: {
+ "Address": nh,
+ "resolvedVia": "connected",
+ "nexthops": {"nexthop1": {"Interfcae": intf}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Nexthop is" " missing in RIB".format(tc_name)
+
+ step(
+ "Shut / no shut IPv4 and IPv6 static next hop interface from"
+ "kernel and FRR CLI"
+ )
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ "After shut of nexthop interface, IPv4 and IPv6 route got removed "
+ "from RIB verify using show ip route show nht"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After no shut route got added again in RIB /FIB using "
+ "show ip route nexthop is resolved using show nht"
+ )
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, "Testcase {} : Failed".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ nh = topo["routers"]["r2"]["links"]["r1-link0"][addr_type].split("/")[0]
+ input_dict_4 = {
+ "r1": {
+ "static_routes": [
+ {
+ "network": ip_list[addr_type][0][2][0],
+ "next_hop": nh,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Removing FRR configured static route verify FRR route also "
+ "removed from FRR"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_with_tag_p0_tc_13_ebgp(request):
+ """
+ Verify static route with tag option
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 links between R1 and R2")
+ step("Configure 1 links between R2 and R3")
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure 2 IPv4 static route (S1 and S2) in R2 with same"
+ "next hop N1 28.1.1.2"
+ )
+ step("Configure static route S1 with tag 1 and static route S2 with" "tag2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S2= ip route 20.1.1.1/24 28.1.1.2 tag 2")
+ step("Enable redistribute static in BGP with route-map")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify routes are present in RIB")
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Configure route-map on R2 with allow tag1 and deny tag2")
+
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "deny",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ # Configure neighbor for route map
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_match_tag_1_ipv4",
+ "direction": "out",
+ }
+ ]
+ }
+ }
+ }
+ },
+ "redistribute": [{"redist_type": "static"}],
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify static route S1 advetised in BGP table when tag1 permit"
+ "in route-map else it is denied"
+ )
+ dut = "r3"
+ input_dict_0 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4002,
+ }
+ ]
+ }
+ }
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_0, protocol=protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is still present in RIB".format(tc_name)
+ )
+
+ dut = "r2"
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is missing in RIB".format(tc_name)
+ )
+
+ step("Modify the route-map to allow tag2 and deny tag1")
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r3"
+ step(
+ "Verify static route S2 advertised in BGP table when tag2"
+ "permit in route-map else it is denied"
+ )
+ protocol = "bgp"
+ input_dict_0 = {
+ "r2": {"static_routes": [{"network": NETWORK2[addr_type], "tag": 4002}]}
+ }
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_0, protocol=protocol)
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ "tag 4002 is missing in RIB".format(tc_name)
+ )
+
+ input_dict_1 = {
+ "r2": {"static_routes": [{"network": NETWORK[addr_type], "tag": 4001}]}
+ }
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_1, protocol=protocol, expected=False
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Route with "
+ "tag 4001 is still present in RIB".format(tc_name)
+ )
+
+ step("Configure one static route with 2 ECMP nexthop N1 and N2")
+ step("For N1 configure tag 1 and for N2 configure tag 2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2")
+ step("configure the route-map to allow tag1 and deny tag 2")
+ step("Modify the route-map to allow tag2 and deny tag1")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "tag": 4002,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("shut/no shut of tag1 and tag2 nexthop")
+
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("configure one static route with 3 next-hop")
+ step("N1-tag1, N2-tag2, N3-tag3")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 1")
+ step("S1= ip route 10.1.1.1/24 29.1.1.2 tag 2")
+ step("S1= ip route 10.1.1.1/24 28.1.1.2 tag 3")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "tag": 4001,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "tag": 4002,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh3"][addr_type],
+ "tag": 4003,
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+
+ step("configure the route-map to allow tag2 & tag3 and deny tag1")
+ # Create route map
+ input_dict_3 = {
+ "r2": {
+ "route_maps": {
+ "rmap_match_tag_1_{}".format(addr_type): [
+ {
+ "action": "deny",
+ "seq_id": 10,
+ "match": {addr_type: {"tag": "4001"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 20,
+ "match": {addr_type: {"tag": "4002"}},
+ },
+ {
+ "action": "permit",
+ "seq_id": 30,
+ "match": {addr_type: {"tag": "4003"}},
+ },
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, input_dict_3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify static route advertised in BGP table with tag3"
+ " nexthop if tag2 is down"
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("shut / no shut of tag2 and tag3 next-hop")
+
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("shut/no shut of tag2 and tag3 nexthop")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ intf = topo["routers"]["r2"]["links"]["r1-link2"]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step("Verify after shut/noshut of nexthop BGP table updated correctly")
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ 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/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
new file mode 100644
index 0000000..3f49ced
--- /dev/null
+++ b/tests/topotests/static_routing_with_ebgp/test_static_routes_topo4_ebgp.py
@@ -0,0 +1,989 @@
+#!/usr/bin/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 in the script.
+
+- Verify static route are blocked from route-map and prefix-list
+ applied in BGP nbrs
+- Verify Static route when FRR connected to 2 TOR
+"""
+
+import sys
+import time
+import os
+import pytest
+import platform
+import ipaddress
+from copy import deepcopy
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ check_address_types,
+ step,
+ create_prefix_lists,
+ create_route_maps,
+ verify_prefix_lists,
+ verify_route_maps,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+ clear_bgp,
+)
+from lib.topojson import build_config_from_json
+
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {}
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+def setup_module(mod):
+ """
+ Set up the pytest environment.
+ * `mod`: module name
+ """
+
+ 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...
+ json_file = "{}/static_routes_topo4_ebgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+def test_static_routes_rmap_pfxlist_p0_tc7_ebgp(request):
+ """
+ Verify static route are blocked from route-map & prefix-list applied in BGP
+ nbrs
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step("Configure holddown timer = 1 keep alive = 3 in all the neighbors")
+ step("verify bgp convergence before starting test case")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Configure 4 IPv4 and 4 IPv6 nbrs with password with mismatch "
+ " authentication between FRR routers "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Api call to modify BGP timers
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {"password": "r2"},
+ "r2-link1": {"password": "r2"},
+ "r2-link2": {"password": "r2"},
+ "r2-link3": {"password": "r2"},
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {"password": "r2"},
+ "r2-link1": {"password": "r2"},
+ "r2-link2": {"password": "r2"},
+ "r2-link3": {"password": "r2"},
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ clear_bgp(tgen, addr_type, "r2")
+
+ step(" All BGP nbrs are down as authentication is mismatch on both" " the sides")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ bgp_convergence is not True
+ ), "Testcase {} : " "Failed \n BGP nbrs must be down. Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Configure 4 IPv4 and 4 IPv6 nbrs with macthing password "
+ " authentication between FRR routers "
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {"password": "r1"},
+ "r2-link1": {"password": "r1"},
+ "r2-link2": {"password": "r1"},
+ "r2-link3": {"password": "r1"},
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {"password": "r1"},
+ "r2-link1": {"password": "r1"},
+ "r2-link2": {"password": "r1"},
+ "r2-link3": {"password": "r1"},
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, deepcopy(input_dict))
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("All BGP nbrs are up as authentication is matched now")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Create prefix list P1 to permit VM3 & deny VM1 v4 & v6 routes")
+ step("Create prefix list P2 to permit VM6 IPv4 and IPv6 routes")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_1_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": topo["routers"]["r2"]["links"]["vm3"][
+ addr_type
+ ],
+ "action": "permit",
+ },
+ {
+ "seqid": 20,
+ "network": topo["routers"]["r2"]["links"]["vm1"][
+ addr_type
+ ],
+ "action": "deny",
+ },
+ ],
+ "pf_list_2_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": topo["routers"]["r2"]["links"]["vm6"][
+ addr_type
+ ],
+ "action": "permit",
+ }
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Prefix list created with matching networks deny or permit "
+ "show ip prefix list"
+ )
+ result = verify_prefix_lists(tgen, input_dict_2)
+ assert result is not True, "Testcase {} : Failed \n" " Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute all the routes (connected, static)")
+ input_dict_2_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute connected in Router BGP")
+
+ input_dict_2_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply prefix list P2 on BGP nbrs 5 & 6 connected from FRR-2")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ clear_bgp_and_verify(tgen, topo, "r2")
+
+ step(
+ "VM1 IPv4 and IPv6 Route which is denied using prefix list is "
+ "not present on FRR1 side routing table , also not able to "
+ "ping the routes show ip route"
+ )
+
+ dut = "r1"
+ protocol = "bgp"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result4 is not True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ step(
+ "VM4 and VM6 IPV4 and IPv6 address are present in local and "
+ "FRR2 routing table show ip bgp show ip route"
+ )
+
+ dut = "r2"
+ ntwk_r2_vm6 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type])
+ ).network
+ )
+ input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, "Testcase {} : Failed.\n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Remove prefix list from all the neighbors")
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ clear_bgp_and_verify(tgen, topo, "r2")
+
+ step("Create RouteMap_1 with prefix list P1 and weight 50")
+ # Create route map
+ rmap_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_pf_list_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"weight": 50},
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, rmap_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create RouteMap_2 with prefix list P2 and weight 50")
+ # Create route map
+ rmap_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_pf_list_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"weight": 50},
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_2_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, rmap_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify Route-map created verify using show route-map")
+ # verify rmap_pf_list_1 and rmap_pf_list_2 are present in router r2
+ input_dict = {
+ "r2": {
+ "route_maps": [
+ "rmap_pf_list_1_{}".format(addr_type),
+ "rmap_pf_list_2_{}".format(addr_type),
+ ]
+ }
+ }
+ result = verify_route_maps(tgen, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply policy RouteMap_1 nbrs 1 2 3 4 to FRR 1")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply policy RouteMap_2 nbrs 5 and 6 to FRR2")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After applying to BGP neighbors verify VM1 IPv4 and IPv6 Route"
+ " which is denied using prefix list is not present on FRR side"
+ " routing table , also not able to ping the routes show ip route"
+ " and VM4 and VM6 IPV4 and IPv6 address are present in local and"
+ " FRR routing table show ip bgp show ip route"
+ )
+
+ dut = "r1"
+ protocol = "bgp"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result4 is not True
+ ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("vm4 should be present in FRR1")
+ dut = "r1"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ step("vm4 should be present in FRR2")
+ dut = "r2"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ ntwk_r2_vm6 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type])
+ ).network
+ )
+ input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed.\n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json
new file mode 100644
index 0000000..99b1973
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo1_ibgp.json
@@ -0,0 +1,157 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json
new file mode 100644
index 0000000..47596a0
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo2_ibgp.json
@@ -0,0 +1,371 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+
+
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+
+
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r2-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ },
+ "redistribute": [{
+ "redist_type": "static"
+ }]
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+
+
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+
+
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link1": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link2": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link3": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link4": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link5": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link6": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ },
+ "r3-link7": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json
new file mode 100644
index 0000000..4e27229
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo3_ibgp.json
@@ -0,0 +1,189 @@
+{
+ "address_types": [
+ "ipv4",
+ "ipv6"
+ ],
+ "ipv4base": "10.0.0.0",
+ "ipv4mask": 30,
+ "ipv6base": "fd00::",
+ "ipv6mask": 64,
+ "link_ip_start": {
+ "ipv4": "10.0.0.0",
+ "v4mask": 29,
+ "ipv6": "fd00::",
+ "v6mask": 64
+ },
+ "lo_prefix": {
+ "ipv4": "1.0.",
+ "v4mask": 32,
+ "ipv6": "2001:db8:f::",
+ "v6mask": 128
+ },
+ "routers": {
+ "r1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link7": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "keepalivetimer": 1,
+ "holddowntimer": 4
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json b/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json
new file mode 100644
index 0000000..bb72578
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/static_routes_topo4_ibgp.json
@@ -0,0 +1,428 @@
+{
+ "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": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm4": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "100",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r1-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r1-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r1-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm6": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r3-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "password": "r1"
+ },
+ "r2-link1": {
+ "password": "r1"
+ },
+ "r2-link2": {
+ "password": "r1"
+ },
+ "r2-link3": {
+ "password": "r1"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "r3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2-link0": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "r2-link3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ },
+ "vm5": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ },
+ "bgp": {
+ "local_as": "300",
+ "address_family": {
+ "ipv4": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "unicast": {
+ "neighbor": {
+ "r2": {
+ "dest_link": {
+ "r3-link0": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link1": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link2": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ },
+ "r3-link3": {
+ "password": "r1",
+ "holddowntimer": 3,
+ "keepalivetimer": 1
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "vm1": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm2": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm3": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm4": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r1": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm5": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r3": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ },
+ "vm6": {
+ "links": {
+ "lo": {
+ "ipv4": "auto",
+ "ipv6": "auto",
+ "type": "loopback"
+ },
+ "r2": {
+ "ipv4": "auto",
+ "ipv6": "auto"
+ }
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py
new file mode 100644
index 0000000..6b7b2ad
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo1_ibgp.py
@@ -0,0 +1,1132 @@
+#!/usr/bin/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.
+#
+"""
+
+ -Verify static route ECMP functionality with 2 next hop
+
+ -Verify static route functionality with 2 next hop and different AD
+ value
+
+ -Verify RIB status when same route advertise via BGP and static route
+
+"""
+import sys
+import time
+import os
+import pytest
+import platform
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": ["11.0.20.1/32", "11.0.20.2/32"], "ipv6": ["2::1/128", "2::2/128"]}
+NETWORK2 = {"ipv4": "11.0.20.1/32", "ipv6": "2::1/128"}
+
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+
+
+def setup_module(mod):
+ """
+
+ Set up the pytest environment.
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo1_ibgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_static_route_2nh_p0_tc_1_ibgp(request):
+ """
+ Verify static route ECMP functionality with 2 next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) and N2 (29.1.1.2) , Static route next-hop present on"
+ "R1"
+ )
+ step("ex :- ip route 10.1.1.1/24 28.1.1.2 & ip route 10.1.1.1/24 29.1.1.1")
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ },
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ },
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using show ip route"
+ " with 2 ECMP next hop "
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the static route configured with nexthop N1 from running config")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from running config")
+
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Only one the nexthops should be active in RIB.")
+
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r3"
+ result = verify_bgp_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ protocol=protocol,
+ next_hop=nh,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r2"
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r2"
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("No shut nexthop interface N2")
+ dut = "r2"
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type], NEXT_HOP_IP["nh2"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " missing in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ dut = "r2"
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Route is" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_2nh_admin_dist_p0_tc_2_ibgp(request):
+ """
+ Verify static route functionality with 2 next hop & different AD value
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ step(
+ "Configure IPv4 static route (10.1.1.1) in R2 with next hop N1"
+ "(28.1.1.2 ) AD 10 and N2 (29.1.1.2) AD 20 , Static route next-hop"
+ "present on R1 \n ex :- ip route 10.1.1.1/24 28.1.1.2 10 & "
+ "ip route 10.1.1.1/24 29.1.1.2 20"
+ )
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ },
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 2 next hop , lowest AD nexthop is active "
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ step("Explicit route is added in R3 for R2 nexthop rechability")
+ rt3_rtes = {
+ "r3": {
+ "static_routes": [
+ {
+ "network": NEXT_HOP_IP["nh1"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ {
+ "network": NEXT_HOP_IP["nh2"][addr_type] + "/32",
+ "next_hop": topo["routers"]["r2"]["links"]["r3"][addr_type],
+ },
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt3_rtes)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("Configure redistribute static in BGP on R2 router")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the static route configured with nexthop N1 from running config")
+ rt1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rt1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N1 , "
+ "route become active with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N1")
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte1_nh1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Remove the static route configured with nexthop N2 from running config")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "On R2, after removing the static route with N2 , "
+ "route become active with nexthop N1 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Configure the static route with nexthop N2")
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, rte2_nh2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Shut nexthop interface N1")
+ intf = topo["routers"]["r2"]["links"]["r1-link0"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("after shut of nexthop N1 , route become active with nexthop N2")
+
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte1_nh1,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte2_nh2, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("No shut the nexthop interface N1")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("Shut nexthop interface N2")
+ intf = topo["routers"]["r2"]["links"]["r1-link1"]["interface"]
+
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step(
+ " after shut of nexthop N1 , route become active with "
+ "nexthop N2 and vice versa."
+ )
+ nh = NEXT_HOP_IP["nh2"][addr_type]
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB".format(tc_name)
+
+ step("No shut nexthop interface N2")
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "after shut of nexthop N1 , route become active "
+ "with nexthop N2 and vice versa."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r2"
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route installed"
+ " in RIB and FIB properly ."
+ )
+ rte1_nh1 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh1"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, rte1_nh1, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte1_nh1, next_hop=nh)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "missing in RIB".format(tc_name)
+
+ rte2_nh2 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh2"][addr_type],
+ "admin_distance": 20,
+ }
+ ]
+ }
+ }
+ nh = [NEXT_HOP_IP["nh2"][addr_type]]
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, rte2_nh2, next_hop=nh)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ rte2_nh2,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes is" "not active in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py
new file mode 100644
index 0000000..350a117
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo2_ibgp.py
@@ -0,0 +1,2033 @@
+#!/usr/bin/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.
+#
+
+
+"""
+ -Verify static route functionality with 8 next hop different AD value
+ and BGP ECMP
+
+ -Verify 8 static route functionality with 8 next hop different AD
+
+ -Verify static route with 8 next hop with different AD value and 8
+ EBGP neighbors
+
+ -Verify static route with 8 next hop with different AD value and 8
+ IBGP neighbors
+
+ -Delete the static route and verify the RIB and FIB state
+
+ -Verify 8 static route functionality with 8 ECMP next hop
+"""
+import sys
+import time
+import os
+import pytest
+import platform
+import random
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+# Import topoJson from lib, to create topology and initial configuration
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+from lib.topotest import version_cmp
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ "11.0.20.6/32",
+ "11.0.20.7/32",
+ "11.0.20.8/32",
+ ],
+ "ipv6": [
+ "2::1/128",
+ "2::2/128",
+ "2::3/128",
+ "2::4/128",
+ "2::5/128",
+ "2::6/128",
+ "2::7/128",
+ "2::8/128",
+ ],
+}
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+PREFIX2 = {"ipv4": "110.0.20.2/32", "ipv6": "20::2/128"}
+NEXT_HOP_IP = []
+topo_diag = """
+ Please view in a fixed-width font such as Courier.
+ +------+ +------+ +------+
+ | +--------------+ +--------------+ |
+ | | | | | |
+ | R1 +---8 links----+ R2 +---8 links----+ R3 |
+ | | | | | |
+ | +--------------+ +--------------+ |
+ +------+ +------+ +------+
+
+"""
+
+
+def setup_module(mod):
+ """
+
+ Set up the pytest environment.
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo2_ibgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_static_rte_with_8ecmp_nh_p1_tc9_ibgp(request):
+ """
+ Verify 8 static route functionality with 8 ECMP next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interfaces / links between R2 and R3")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure 8 IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) , N2(22.1.1.2) , N3(23.1.1.2) , N4(24.1.1.2) ,"
+ "N5(25.1.1.2) , N6(26.1.1.2) , N7(27.1.1.2) , N8(28.1.1.2) ,"
+ "Static route next-hop present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh_all[addr_type] = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ dut = "r2"
+ protocol = "static"
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "verify that entry is removed from RIB and FIB of R3 "
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed\nError: Routes is" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed\nError: Routes are" " missing in RIB".format(
+ tc_name
+ )
+
+ protocol = "static"
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ # Shutdown interface
+ dut = "r2"
+ step(
+ " interface which is about to be shut no shut between r1 and r2 is %s",
+ topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"],
+ )
+ intf = topo["routers"]["r2"]["links"]["r1-link{}".format(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+
+ step("Random no shut of the nexthop interfaces")
+ # Bringup interface
+ shutdown_bringup_interface(tgen, dut, intf, True)
+
+ step(
+ "After random shut/no shut of nexthop , only that "
+ "nexthop deleted/added from all the routes , other nexthop remain "
+ "unchanged"
+ )
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ dut = "r2"
+ randnum = random.randint(1, 7)
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After delete of random route , that route only got deleted from"
+ " RIB/FIB other route are showing properly"
+ )
+ nh = NEXT_HOP_IP["nh{}".format(randnum)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router , static route "
+ "installed in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ nhp = 1
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Verifying %s routes on r2", addr_type)
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the BGP neighbor or redistribute static knob , "
+ "verify route got clear from RIB and FIB of R3 routes "
+ )
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc6_ibgp(request):
+ """
+ Verify static route functionality with 8 next hop different AD
+ value and BGP ECMP
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2 ,")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step(
+ "Configure IBGP IPv4 peering over loopback interface between"
+ "R2 and R3 router."
+ )
+ step("Configure redistribute static in BGP on R2 router")
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ retry_timeout=6,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are present in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ retry_timeout=6,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "bgp"
+ # this is next hop reachability route in r3 as we are using ibgp
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ nh_as_rte = NEXT_HOP_IP["nh1"][addr_type] + "/32"
+ # add static routes
+ nh_static_rte = {
+ "r3": {"static_routes": [{"network": nh_as_rte, "next_hop": "Null0"}]}
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, nh_static_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After each interface shut and no shut between R2 -R3 ,verify static"
+ "route is present in the RIB & FIB of R3 & R2 RIB/FIB is remain"
+ " unchanged"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "static"
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "bgp"
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("BGP neighbor remove and add")
+ for rtr in ["r2", "r3"]:
+ if "bgp" in topo["routers"][rtr].keys():
+ delete_bgp = {rtr: {"bgp": {"delete": True}}}
+ result = create_router_bgp(tgen, topo, delete_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ create_router_bgp(tgen, topo["routers"])
+
+ NEXT_HOP_IP = populate_nh()
+ step("Verify routes are still present after delete and add bgp")
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify that routes are deleted from R3 routing table")
+
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes are"
+ " strill present in RIB of R3".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_ibgp_ecmp_p1_tc7_ibgp(request):
+ """
+ Verify static route with 8 next hop with different AD value and 8
+ IBGP neighbors
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ step("Configure 8 interfaces / links between R1 and R2")
+ step("Configure 8 interlaces/links between R2 and R3")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3")
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ dut = "r2"
+ protocol = "bgp"
+
+ # this is next hop reachability route in r3 as we are using ibgp
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ nh_as_rte = NEXT_HOP_IP["nh1"][addr_type] + "/32"
+ # add static routes
+ nh_static_rte = {
+ "r3": {"static_routes": [{"network": nh_as_rte, "next_hop": "Null0"}]}
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, nh_static_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After each interface shut and no shut between R2 -R3 ,verify static"
+ "route is present in the RIB & FIB of R3 & R2 RIB/FIB is remain"
+ " unchanged"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "static"
+ dut = "r2"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name)
+
+ protocol = "bgp"
+ dut = "r3"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {}: Failed \n Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("BGP neighbor remove and add")
+ for rtr in ["r2", "r3"]:
+ if "bgp" in topo["routers"][rtr].keys():
+ delete_bgp = {rtr: {"bgp": {"delete": True}}}
+ result = create_router_bgp(tgen, topo, delete_bgp)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ create_router_bgp(tgen, topo["routers"])
+
+ NEXT_HOP_IP = populate_nh()
+ step("Verify routes are still present after delete and add bgp")
+ dut = "r2"
+ protocol = "static"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify that routes are deleted from R3 routing table")
+
+ dut = "r3"
+ protocol = "bgp"
+ input_dict_4 = {"r2": {"static_routes": [{"network": PREFIX1[addr_type]}]}}
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB of R3".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_8nh_diff_AD_bgp_ecmp_p1_tc10_ibgp(request):
+ """
+ Verify 8 static route functionality with 8 next hop different AD'
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2 ")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80"
+ )
+ step(
+ "Configure nexthop AD in such way for static route S1 , N1 is"
+ "preferred and for S2 , N2 is preferred and so on .."
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Verify that highest AD nexthop are inactive")
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ retry_timeout=6,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop which"
+ "got removed is not shown in RIB and FIB"
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ " After configuring them, route is always active with lowest AD"
+ " value and all the nexthop populated in RIB and FIB again"
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ }
+ ]
+ }
+ }
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ "lowest AD is missing in RIB".format(tc_name)
+ )
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Remove random static route with all the nexthop")
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one , "
+ "route become active with next preferred nexthop and nexthop "
+ "which got removed is not shown in RIB and FIB"
+ )
+ nh = NEXT_HOP_IP["nh" + str(nhp)][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Route "
+ " is still present in RIB".format(tc_name)
+ )
+
+ step("Reconfigure the deleted routes and verify they are installed")
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, protocol=protocol)
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route "
+ " is still present in RIB".format(tc_name)
+ )
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+
+ start_router(tgen, "r2")
+
+ step("After reloading, verify that routes are still present in R2.")
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ second_rte,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Route is missing in RIB".format(tc_name)
+
+ step("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the BGP neighbor or redistribute static knob , "
+ "verify route got clear from RIB and FIB of R3 routes "
+ )
+ dut = "r3"
+ protocol = "bgp"
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_static_route_delete_p0_tc11_ibgp(request):
+ """
+ Delete the static route and verify the RIB and FIB state
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2 ")
+ step("Configure 8 IBGP IPv4 peering between R2 and R3 router.")
+ reset_config_on_routers(tgen)
+
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80"
+ )
+ step(
+ "Configure nexthop AD in such way for static route S1 , N1 is"
+ "preferred and for S2 , N2 is preferred and so on .."
+ )
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop , lowest AD nexthop is active"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Verify that highest AD nexthop are inactive")
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes " " are missing in RIB".format(tc_name)
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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("Remove the redistribute static knob")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Verify after removing the redistribute static from BGP all the"
+ "routes got delete from RIB and FIB of R3 "
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, protocol=protocol, expected=False
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ second_rte = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX2[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, second_rte)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ " After removing all the routes and nexthop from R2 , "
+ " verify R2 RIB and FIB is cleared"
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still active in RIB".format(
+ tc_name
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
new file mode 100644
index 0000000..cdd7e13
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo3_ibgp.py
@@ -0,0 +1,891 @@
+#!/usr/bin/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.
+#
+"""
+ -Verify static route ECMP functionality with 8 next hop
+
+ -Verify static route functionality with 8 next hop different AD value
+
+ -Verify static route with tag option
+
+ -Verify BGP did not install the static route when it receive route
+ with local next hop
+
+"""
+import sys
+import time
+import os
+import pytest
+import platform
+import random
+
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+from lib.topotest import version_cmp
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ create_static_routes,
+ check_address_types,
+ step,
+ shutdown_bringup_interface,
+ stop_router,
+ start_router,
+)
+from lib.topolog import logger
+from lib.bgp import verify_bgp_convergence, create_router_bgp, verify_bgp_rib
+from lib.topojson import build_config_from_json
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {
+ "ipv4": [
+ "11.0.20.1/32",
+ "11.0.20.2/32",
+ "11.0.20.3/32",
+ "11.0.20.4/32",
+ "11.0.20.5/32",
+ "11.0.20.6/32",
+ "11.0.20.7/32",
+ "11.0.20.8/32",
+ ],
+ "ipv6": [
+ "2::1/128",
+ "2::2/128",
+ "2::3/128",
+ "2::4/128",
+ "2::5/128",
+ "2::6/128",
+ "2::7/128",
+ "2::8/128",
+ ],
+}
+PREFIX1 = {"ipv4": "110.0.20.1/32", "ipv6": "20::1/128"}
+NETWORK2 = {"ipv4": ["11.0.20.1/32"], "ipv6": ["2::1/128"]}
+NEXT_HOP_IP = []
+
+
+def setup_module(mod):
+ """
+
+ Set up the pytest environment.
+
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo3_ibgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment
+
+ * `mod`: module name
+ """
+
+ 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)
+
+
+def populate_nh():
+ NEXT_HOP_IP = {
+ "nh1": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link0"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link0"]["ipv6"].split("/")[0],
+ },
+ "nh2": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link1"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link1"]["ipv6"].split("/")[0],
+ },
+ "nh3": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link2"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link2"]["ipv6"].split("/")[0],
+ },
+ "nh4": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link3"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link3"]["ipv6"].split("/")[0],
+ },
+ "nh5": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link4"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link4"]["ipv6"].split("/")[0],
+ },
+ "nh6": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link5"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link5"]["ipv6"].split("/")[0],
+ },
+ "nh7": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link6"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link6"]["ipv6"].split("/")[0],
+ },
+ "nh8": {
+ "ipv4": topo["routers"]["r1"]["links"]["r2-link7"]["ipv4"].split("/")[0],
+ "ipv6": topo["routers"]["r1"]["links"]["r2-link7"]["ipv6"].split("/")[0],
+ },
+ }
+ return NEXT_HOP_IP
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+
+
+def test_staticroute_with_ecmp_p0_tc3_ibgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop'
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+ NEXT_HOP_IP = populate_nh()
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2), N2(22.1.1.2), N3(23.1.1.2), N4(24.1.1.2),"
+ "N5(25.1.1.2), N6(26.1.1.2), N7(27.1.1.2),N8(28.1.1.2), Static"
+ "route next-hop present on R1"
+ )
+
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+ nh = [
+ NEXT_HOP_IP["nh1"][addr_type],
+ NEXT_HOP_IP["nh2"][addr_type],
+ NEXT_HOP_IP["nh3"][addr_type],
+ NEXT_HOP_IP["nh4"][addr_type],
+ NEXT_HOP_IP["nh5"][addr_type],
+ NEXT_HOP_IP["nh6"][addr_type],
+ NEXT_HOP_IP["nh7"][addr_type],
+ NEXT_HOP_IP["nh8"][addr_type],
+ ]
+
+ dut = "r2"
+ protocol = "static"
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes are" " missing in RIB".format(tc_name)
+
+ write_test_footer(tc_name)
+
+
+def test_staticroute_with_ecmp_with_diff_AD_p0_tc4_ibgp(request):
+ """
+ Verify static route ECMP functionality with 8 next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ reset_config_on_routers(tgen)
+
+ step("Configure 8 interfaces / links between R1 and R2,")
+ step("Configure IBGP IPv4 peering between R2 and R3 router.")
+ NEXT_HOP_IP = populate_nh()
+ nh_all = {}
+ for addr_type in ADDR_TYPES:
+ nh_all[addr_type] = []
+ for nhp in range(1, 9):
+ nh_all[addr_type].append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ step(
+ "Configure IPv4 static route in R2 with 8 next hop"
+ "N1(21.1.1.2) AD 10, N2(22.1.1.2) AD 20, N3(23.1.1.2) AD 30,"
+ "N4(24.1.1.2) AD 40, N5(25.1.1.2) AD 50, N6(26.1.1.2) AD 60,"
+ "N7(27.1.1.2) AD 70, N8(28.1.1.2) AD 80, Static route next-hop"
+ "present on R1"
+ )
+ for addr_type in ADDR_TYPES:
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ logger.info("Verifying %s routes on r2", addr_type)
+
+ step(
+ "On R2, static route installed in RIB using "
+ "show ip route with 8 next hop, lowest AD nexthop is active"
+ )
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ step("Configure redistribute static in BGP on R2 router")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Configuring redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After configuring them, route is always active with lowest AD"
+ "value and all the nexthop populated in RIB and FIB again "
+ )
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ step(
+ "Remove the static route configured with nexthop N1 to N8, one"
+ "by one from running config"
+ )
+
+ for addr_type in ADDR_TYPES:
+ # delete static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ "delete": True,
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After removing the static route with N1 to N8 one by one, "
+ "route become active with next preferred nexthop and nexthop which "
+ "got removed is not shown in RIB and FIB"
+ )
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh_all[addr_type],
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \nError: Routes are" " still present in RIB".format(
+ tc_name
+ )
+
+ step("Configure the static route with nexthop N1 to N8, one by" "one")
+ for addr_type in ADDR_TYPES:
+ # add static routes
+ for nhp in range(1, 9):
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(nhp)][addr_type],
+ "admin_distance": 10 * nhp,
+ }
+ ]
+ }
+ }
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("On R2, static route with lowest AD nexthop installed in FIB")
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ step("Random shut of the nexthop interfaces")
+ randnum = random.randint(0, 7)
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, False)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ input_dict_5 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type],
+ }
+ ]
+ }
+ }
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_5,
+ next_hop=nhip,
+ protocol=protocol,
+ expected=False,
+ )
+ assert (
+ result is not True
+ ), "Testcase {} : Failed \n" "Error: Routes are still present in RIB".format(
+ tc_name
+ )
+
+ step("Random no shut of the nexthop interfaces")
+ for addr_type in ADDR_TYPES:
+ intf = topo["routers"]["r2"]["links"]["r1-link" + str(randnum)]["interface"]
+ shutdown_bringup_interface(tgen, dut, intf, True)
+ nhip = NEXT_HOP_IP["nh" + str(randnum + 1)][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_5, next_hop=nhip, protocol=protocol
+ )
+ assert (
+ result is True
+ ), "Testcase {} : Failed \n" "Error: Routes are missing in RIB".format(tc_name)
+
+ step("Reload the FRR router")
+ # stop/start -> restart FRR router and verify
+ stop_router(tgen, "r2")
+ start_router(tgen, "r2")
+
+ step(
+ "After reload of FRR router, static route installed "
+ "in RIB and FIB properly ."
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": PREFIX1[addr_type],
+ "next_hop": NEXT_HOP_IP["nh1"][addr_type],
+ "admin_distance": 10,
+ }
+ ]
+ }
+ }
+ dut = "r2"
+ protocol = "static"
+ nh = NEXT_HOP_IP["nh1"][addr_type]
+ result = verify_rib(
+ tgen, addr_type, dut, input_dict_4, next_hop=nh, protocol=protocol, fib=True
+ )
+ assert result is True, (
+ "Testcase {} : Failed \nError: Route with "
+ " lowest AD is missing in RIB".format(tc_name)
+ )
+
+ nh = []
+ for nhp in range(2, 9):
+ nh.append(NEXT_HOP_IP["nh" + str(nhp)][addr_type])
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ fib=True,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes "
+ " with high AD are active in RIB".format(tc_name)
+ )
+
+ step("Remove the redistribute static knob")
+
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "redistribute": [
+ {"redist_type": "static", "delete": True}
+ ]
+ }
+ }
+ }
+ }
+ }
+ }
+
+ logger.info("Remove redistribute static")
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+ step("verify that routes are deleted from R3 routing table")
+ dut = "r3"
+ protocol = "bgp"
+ result = verify_rib(
+ tgen,
+ addr_type,
+ dut,
+ input_dict_4,
+ next_hop=nh,
+ protocol=protocol,
+ expected=False,
+ )
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes are"
+ " still present in RIB of R3".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+def test_bgp_local_nexthop_p1_tc14_ibgp(request):
+ """
+ Verify BGP did not install the static route when it receive route
+ with local next hop
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+
+ step("Configure BGP IPv4 session between R2 and R3")
+ step("Configure IPv4 static route on R2")
+ reset_config_on_routers(tgen)
+
+ for addr_type in ADDR_TYPES:
+ # Enable static routes
+ input_dict_4 = {
+ "r2": {
+ "static_routes": [
+ {
+ "network": NETWORK[addr_type],
+ "next_hop": topo["routers"]["r3"]["links"]["r2-link0"][
+ addr_type
+ ].split("/")[0],
+ }
+ ]
+ }
+ }
+
+ logger.info("Configure static routes")
+ result = create_static_routes(tgen, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Configure redistribute static in the BGP")
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "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 R2 BGP table has IPv4 route")
+ dut = "r2"
+ result = verify_rib(tgen, addr_type, dut, input_dict_4)
+ assert (
+ result is True
+ ), "Testcase {} : Failed \nError: Routes is" " missing in RIB of R2".format(
+ tc_name
+ )
+
+ step(" Verify route did not install in the R3 BGP table, RIB/FIB")
+ dut = "r3"
+ result = verify_bgp_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes is"
+ " still present in BGP RIB of R2".format(tc_name)
+ )
+
+ result = verify_rib(tgen, addr_type, dut, input_dict_4, expected=False)
+ assert result is not True, (
+ "Testcase {} : Failed \nError: Routes is"
+ " still present in RIB of R2".format(tc_name)
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py
new file mode 100644
index 0000000..d5e5148
--- /dev/null
+++ b/tests/topotests/static_routing_with_ibgp/test_static_routes_topo4_ibgp.py
@@ -0,0 +1,985 @@
+#!/usr/bin/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 in the script.
+
+- Verify static route are blocked from route-map and prefix-list
+ applied in BGP nbrs
+"""
+
+import sys
+import time
+import os
+import pytest
+import platform
+import ipaddress
+from copy import deepcopy
+
+# 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/"))
+# pylint: disable=C0413
+# Import topogen and topotest helpers
+from lib.topogen import Topogen, get_topogen
+
+from lib.common_config import (
+ start_topology,
+ write_test_header,
+ write_test_footer,
+ reset_config_on_routers,
+ verify_rib,
+ check_address_types,
+ step,
+ create_prefix_lists,
+ create_route_maps,
+ verify_prefix_lists,
+ verify_route_maps,
+)
+from lib.topolog import logger
+from lib.bgp import (
+ verify_bgp_convergence,
+ create_router_bgp,
+ clear_bgp_and_verify,
+ clear_bgp,
+)
+from lib.topojson import build_config_from_json
+from lib.topotest import version_cmp
+
+
+# Global variables
+BGP_CONVERGENCE = False
+ADDR_TYPES = check_address_types()
+NETWORK = {"ipv4": "2.2.2.2/32", "ipv6": "22:22::2/128"}
+NEXT_HOP_IP = {}
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.staticd]
+
+
+def setup_module(mod):
+ """
+ Set up the pytest environment.
+ * `mod`: module name
+ """
+ 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...
+ json_file = "{}/static_routes_topo4_ibgp.json".format(CWD)
+ tgen = Topogen(json_file, mod.__name__)
+ global topo
+ topo = tgen.json_topo
+ # ... and here it calls Mininet initialization functions.
+
+ # Starting topology, create tmp files which are loaded to routers
+ # to start daemons 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 = (
+ 'These tests will not run. (have kernel "{}", '
+ "requires kernel >= 4.19)".format(platform.release())
+ )
+ pytest.skip(error_msg)
+
+ # Checking BGP convergence
+ global BGP_CONVERGENCE
+ global ADDR_TYPES
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ # Api call verify whether BGP is converged
+ BGP_CONVERGENCE = verify_bgp_convergence(tgen, topo)
+ assert BGP_CONVERGENCE is True, "setup_module :Failed \n Error: {}".format(
+ BGP_CONVERGENCE
+ )
+
+ logger.info("Running setup_module() done")
+
+
+def teardown_module(mod):
+ """
+ Teardown the pytest environment.
+
+ * `mod`: module name
+ """
+ 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)
+
+
+#####################################################
+#
+# Tests starting
+#
+#####################################################
+def test_static_routes_rmap_pfxlist_p0_tc7_ibgp(request):
+ """
+ Verify static route are blocked from route-map & prefix-list applied in BGP
+ nbrs
+
+ """
+ tc_name = request.node.name
+ write_test_header(tc_name)
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ reset_config_on_routers(tgen)
+ step("Configure holddown timer = 1 keep alive = 3 in all the neighbors")
+ step("verify bgp convergence before starting test case")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Configure 4 IPv4 and 4 IPv6 nbrs with password with mismatch "
+ " authentication between FRR routers "
+ )
+
+ for addr_type in ADDR_TYPES:
+ # Api call to modify BGP timerse
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {"password": "r2"},
+ "r2-link1": {"password": "r2"},
+ "r2-link2": {"password": "r2"},
+ "r2-link3": {"password": "r2"},
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {"password": "r2"},
+ "r2-link1": {"password": "r2"},
+ "r2-link2": {"password": "r2"},
+ "r2-link3": {"password": "r2"},
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, deepcopy(input_dict))
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+ clear_bgp(tgen, addr_type, "r2")
+
+ step(" All BGP nbrs are down as authentication is mismatch on both" " the sides")
+
+ bgp_convergence = verify_bgp_convergence(tgen, topo, expected=False)
+ assert (
+ bgp_convergence is not True
+ ), "Testcase {} : " "Failed \n BGP nbrs must be down. Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step(
+ "Configure 4 IPv4 and 4 IPv6 nbrs with macthing password "
+ " authentication between FRR routers "
+ )
+ for addr_type in ADDR_TYPES:
+ input_dict = {
+ "r2": {
+ "bgp": {
+ "local_as": "200",
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {"password": "r1"},
+ "r2-link1": {"password": "r1"},
+ "r2-link2": {"password": "r1"},
+ "r2-link3": {"password": "r1"},
+ }
+ },
+ "r3": {
+ "dest_link": {
+ "r2-link0": {"password": "r1"},
+ "r2-link1": {"password": "r1"},
+ "r2-link2": {"password": "r1"},
+ "r2-link3": {"password": "r1"},
+ }
+ },
+ }
+ }
+ }
+ },
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, deepcopy(input_dict))
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("All BGP nbrs are up as authentication is matched now")
+ bgp_convergence = verify_bgp_convergence(tgen, topo)
+ assert bgp_convergence is True, "Testcase {} : Failed \n " "Error: {}".format(
+ tc_name, bgp_convergence
+ )
+
+ step("Create prefix list P1 to permit VM3 & deny VM1 v4 & v6 routes")
+ step("Create prefix list P2 to permit VM6 IPv4 and IPv6 routes")
+ for addr_type in ADDR_TYPES:
+ input_dict_2 = {
+ "r2": {
+ "prefix_lists": {
+ addr_type: {
+ "pf_list_1_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": topo["routers"]["r2"]["links"]["vm3"][
+ addr_type
+ ],
+ "action": "permit",
+ },
+ {
+ "seqid": 20,
+ "network": topo["routers"]["r2"]["links"]["vm1"][
+ addr_type
+ ],
+ "action": "deny",
+ },
+ ],
+ "pf_list_2_{}".format(addr_type): [
+ {
+ "seqid": 10,
+ "network": topo["routers"]["r2"]["links"]["vm6"][
+ addr_type
+ ],
+ "action": "permit",
+ }
+ ],
+ }
+ }
+ }
+ }
+ result = create_prefix_lists(tgen, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "Prefix list created with matching networks deny or permit "
+ "show ip prefix list"
+ )
+ result = verify_prefix_lists(tgen, input_dict_2)
+ assert result is not True, "Testcase {} : Failed \n" " Error: {}".format(
+ tc_name, result
+ )
+
+ step("Redistribute all the routes (connected, static)")
+ input_dict_2_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "static"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("configure redistribute connected in Router BGP")
+
+ input_dict_2_r1 = {
+ "r1": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2_r3 = {
+ "r3": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2_r3)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_2 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {"redistribute": [{"redist_type": "connected"}]}
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_2)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply prefix list P1 on BGP neighbors 1 2 3 4 connected from " "frr r1")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply prefix list P2 on BGP nbrs 5 & 6 connected from FRR-2")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ clear_bgp_and_verify(tgen, topo, "r2")
+
+ step(
+ "VM1 IPv4 and IPv6 Route which is denied using prefix list is "
+ "not present on FRR1 side routing table , also not able to "
+ "ping the routes show ip route"
+ )
+
+ dut = "r1"
+ protocol = "bgp"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert result4 is not True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ step(
+ "VM4 and VM6 IPV4 and IPv6 address are present in local and "
+ "FRR2 routing table show ip bgp show ip route"
+ )
+
+ dut = "r2"
+ ntwk_r2_vm6 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type])
+ ).network
+ )
+ input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, "Testcase {} : Failed.\n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("Remove prefix list from all the neighbors")
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_1_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link1": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link2": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ "r2-link3": {
+ "prefix_lists": [
+ {
+ "name": "pf_list_2_{}".format(
+ addr_type
+ ),
+ "direction": "out",
+ "delete": True,
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ clear_bgp_and_verify(tgen, topo, "r2")
+
+ step("Create RouteMap_1 with prefix list P1 and weight 50")
+ # Create route map
+ rmap_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_pf_list_1_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"weight": 50},
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_1_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, rmap_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Create RouteMap_2 with prefix list P2 and weight 50")
+ # Create route map
+ rmap_dict = {
+ "r2": {
+ "route_maps": {
+ "rmap_pf_list_2_{}".format(addr_type): [
+ {
+ "action": "permit",
+ "set": {"weight": 50},
+ "match": {
+ addr_type: {
+ "prefix_lists": "pf_list_2_{}".format(addr_type)
+ }
+ },
+ }
+ ]
+ }
+ }
+ }
+ result = create_route_maps(tgen, rmap_dict)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify Route-map created verify using show route-map")
+ # verify rmap_pf_list_1 and rmap_pf_list_2 are present in router r2
+ input_dict = {
+ "r2": {
+ "route_maps": [
+ "rmap_pf_list_1_{}".format(addr_type),
+ "rmap_pf_list_2_{}".format(addr_type),
+ ]
+ }
+ }
+ result = verify_route_maps(tgen, input_dict, expected=False)
+ assert result is not True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply policy RouteMap_1 nbrs 1 2 3 4 to FRR 1")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r1": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_1_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Apply policy RouteMap_2 nbrs 5 and 6 to FRR2")
+ # Configure prefix list to bgp neighbor
+ input_dict_4 = {
+ "r2": {
+ "bgp": {
+ "address_family": {
+ addr_type: {
+ "unicast": {
+ "neighbor": {
+ "r3": {
+ "dest_link": {
+ "r2-link0": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link1": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link2": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ "r2-link3": {
+ "route_maps": [
+ {
+ "name": "rmap_pf_list_2_"
+ "{}".format(addr_type),
+ "direction": "out",
+ }
+ ]
+ },
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ result = create_router_bgp(tgen, topo, input_dict_4)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step(
+ "After applying to BGP neighbors verify VM1 IPv4 and IPv6 Route"
+ " which is denied using prefix list is not present on FRR side"
+ " routing table , also not able to ping the routes show ip route"
+ " and VM4 and VM6 IPV4 and IPv6 address are present in local and"
+ " FRR routing table show ip bgp show ip route"
+ )
+
+ dut = "r1"
+ protocol = "bgp"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm1"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(
+ tgen, addr_type, dut, input_dict, protocol=protocol, expected=False
+ )
+ assert (
+ result4 is not True
+ ), "Testcase {} : Failed \n" "routes are still present \n Error: {}".format(
+ tc_name, result4
+ )
+
+ step("vm4 should be present in FRR1")
+ dut = "r1"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ step("vm4 should be present in FRR2")
+ dut = "r2"
+ ntwk_r2_vm1 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r1"]["links"]["vm4"][addr_type])
+ ).network
+ )
+ input_dict = {"r1": {"static_routes": [{"network": ntwk_r2_vm1}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict)
+ assert result4 is True, (
+ "Testcase {} : Failed , VM1 route is "
+ "not filtered out via prefix list. \n Error: {}".format(tc_name, result4)
+ )
+
+ dut = "r3"
+ protocol = "bgp"
+ ntwk_r2_vm6 = str(
+ ipaddress.ip_interface(
+ u"{}".format(topo["routers"]["r2"]["links"]["vm6"][addr_type])
+ ).network
+ )
+ input_dict = {"r3": {"static_routes": [{"network": ntwk_r2_vm6}]}}
+ result4 = verify_rib(tgen, addr_type, dut, input_dict, protocol=protocol)
+ assert result4 is True, "Testcase {} : Failed.\n Error: {}".format(
+ tc_name, result4
+ )
+
+ write_test_footer(tc_name)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/subdir.am b/tests/topotests/subdir.am
new file mode 100644
index 0000000..e489f92
--- /dev/null
+++ b/tests/topotests/subdir.am
@@ -0,0 +1,7 @@
+TOPOTESTS_DIR = tests/topotests
+
+topotests-build: ## Builds docker images for topotests
+ $(TOPOTESTS_DIR)/docker/build.sh
+
+topotests: ## Runs topotests
+ $(TOPOTESTS_DIR)/docker/frr-topotests.sh
diff --git a/tests/topotests/zebra_multiple_connected/r1/ip_route.json b/tests/topotests/zebra_multiple_connected/r1/ip_route.json
new file mode 100644
index 0000000..c29f2f9
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/r1/ip_route.json
@@ -0,0 +1,62 @@
+{
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.1.1/32":[
+ {
+ "prefix":"192.168.1.1/32",
+ "prefixLen":32,
+ "protocol":"kernel",
+ "vrfName":"default",
+ "selected":true,
+ "destSelected":true,
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.99",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_multiple_connected/r1/ip_route2.json b/tests/topotests/zebra_multiple_connected/r1/ip_route2.json
new file mode 100644
index 0000000..2699565
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/r1/ip_route2.json
@@ -0,0 +1,102 @@
+{
+ "10.0.1.0/24":[
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"10.0.1.0/24",
+ "prefixLen":24,
+ "protocol":"connected",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.0.1.30/32":[
+ {
+ "prefix":"10.0.1.30/32",
+ "prefixLen":32,
+ "protocol":"kernel",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "directlyConnected":true,
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "10.9.9.0/24":[
+ {
+ "prefix":"10.9.9.0/24",
+ "prefixLen":24,
+ "protocol":"kernel",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.30",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ],
+ "192.168.1.1/32":[
+ {
+ "prefix":"192.168.1.1/32",
+ "prefixLen":32,
+ "protocol":"kernel",
+ "vrfName":"default",
+ "distance":0,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "fib":true,
+ "ip":"10.0.1.99",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_multiple_connected/r1/zebra.conf b/tests/topotests/zebra_multiple_connected/r1/zebra.conf
new file mode 100644
index 0000000..81adcad
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/r1/zebra.conf
@@ -0,0 +1,9 @@
+interface r1-eth0
+ ip address 10.0.1.1/24
+!
+interface r1-eth1
+ ip address 10.0.1.2/24
+!
+interface r1-eth2
+ ip address 10.0.1.3/24
+! \ No newline at end of file
diff --git a/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py
new file mode 100644
index 0000000..8882cf5
--- /dev/null
+++ b/tests/topotests/zebra_multiple_connected/test_zebra_multiple_connected.py
@@ -0,0 +1,162 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_multiple_connected.py
+#
+# Copyright (c) 2022 by
+# Nvidia Corporation
+# Donald Sharp
+#
+# 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_multiple_connected.py: Testing multiple connected
+
+"""
+
+import os
+import re
+import sys
+import pytest
+import json
+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.
+
+#####################################################
+##
+## Network Topology Definition
+##
+#####################################################
+
+
+def build_topo(tgen):
+ for routern in range(1, 4):
+ tgen.add_router("r{}".format(routern))
+
+ # On main router
+ # First switch is for a dummy interface (for local network)
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+ # Switches for zebra
+ # switch 2 switch is for connection to zebra router
+ switch = tgen.add_switch("sw2")
+ switch.add_link(tgen.gears["r1"])
+ switch.add_link(tgen.gears["r2"])
+
+ # switch 4 is stub on remote zebra router
+ switch = tgen.add_switch("sw4")
+ switch.add_link(tgen.gears["r3"])
+
+ # switch 3 is between zebra routers
+ switch = tgen.add_switch("sw3")
+ switch.add_link(tgen.gears["r2"])
+ switch.add_link(tgen.gears["r3"])
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+def setup_module(module):
+ "Setup topology"
+ tgen = Topogen(build_topo, module.__name__)
+ tgen.start_topology()
+
+ # This is a sample of configuration loading.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+
+ 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_connected_multiple():
+ "Test multiple connected routes that have a kernel route pointing at one"
+
+ tgen = get_topogen()
+ # Don't run this test if we have any failure.
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+ router.run("ip route add 192.168.1.1/32 via 10.0.1.99 dev r1-eth1")
+ router.run("ip link add dummy1 type dummy")
+ router.run("ip link set dummy1 up")
+ router.run("ip link set dummy1 down")
+
+ routes = "{}/{}/ip_route.json".format(CWD, router.name)
+ expected = json.loads(open(routes).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "Kernel route is missing from zebra"
+
+
+def test_zebra_system_recursion():
+ "Test a system route recursing through another system route"
+
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ router = tgen.gears["r1"]
+ router.run("ip route add 10.0.1.30/32 dev r1-eth1")
+ router.run("ip route add 10.9.9.0/24 via 10.0.1.30 dev r1-eth1")
+ router.run("ip link add dummy2 type dummy")
+ router.run("ip link set dummy2 up")
+ router.run("ip link set dummy2 down")
+
+ routes = "{}/{}/ip_route2.json".format(CWD, router.name)
+ expected = json.loads(open(routes).read())
+ test_func = partial(
+ topotest.router_json_cmp, router, "show ip route json", expected
+ )
+
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=1)
+ assert result is None, "Kernel route is missing from zebra"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_netlink/__init__.py b/tests/topotests/zebra_netlink/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/zebra_netlink/__init__.py
diff --git a/tests/topotests/zebra_netlink/r1/zebra.conf b/tests/topotests/zebra_netlink/r1/zebra.conf
new file mode 100644
index 0000000..786be19
--- /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 100644
index 0000000..ca90c5c
--- /dev/null
+++ b/tests/topotests/zebra_netlink/test_zebra_netlink.py
@@ -0,0 +1,120 @@
+#!/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
+
+"""
+# pylint: disable=C0413
+import ipaddress
+import json
+import sys
+from functools import partial
+
+import pytest
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter
+from lib.topolog import logger
+
+
+pytestmark = [pytest.mark.sharpd]
+
+
+#####################################################
+##
+## Tests starting
+##
+#####################################################
+
+
+@pytest.fixture(scope="module")
+def tgen(request):
+ "Sets up the pytest environment"
+
+ topodef = {"s1": ("r1")}
+ tgen = Topogen(topodef, request.module.__name__)
+ tgen.start_topology()
+
+ # Initialize all routers.
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ router.load_config(TopoRouter.RD_ZEBRA, "zebra.conf")
+ router.load_config(TopoRouter.RD_SHARP)
+
+ tgen.start_router()
+ yield tgen
+ tgen.stop_topology()
+
+
+@pytest.fixture(autouse=True)
+def skip_on_failure(tgen):
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+
+def test_zebra_netlink_batching(tgen):
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ logger.info(
+ "Test the situation where dataplane fills netlink send buffer entirely."
+ )
+ 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")
+
+ count = 100
+ r1.vtysh_cmd("sharp install routes 2.1.3.7 nexthop 192.168.1.1 " + str(count))
+
+ # Generate expected results
+ entry = {
+ "protocol": "sharp",
+ "distance": 150,
+ "metric": 0,
+ "installed": True,
+ "table": 254,
+ "nexthops": [
+ {
+ "fib": True,
+ "ip": "192.168.1.1",
+ "afi": "ipv4",
+ "interfaceName": "r1-eth0",
+ "active": True,
+ "weight": 1,
+ }
+ ],
+ }
+
+ match = {}
+ base = int(ipaddress.ip_address(u"2.1.3.7"))
+ for i in range(base, base + count):
+ pfx = str(ipaddress.ip_network((i, 32)))
+ match[pfx] = [dict(entry, prefix=pfx)]
+
+ ok = topotest.router_json_cmp_retry(r1, "show ip route json", match)
+ assert ok, '"r1" JSON output mismatches'
+
+ r1.vtysh_cmd("sharp remove routes 2.1.3.7 " + str(count))
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_nht_resolution/r1/sharpd.conf b/tests/topotests/zebra_nht_resolution/r1/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/zebra_nht_resolution/r1/sharpd.conf
diff --git a/tests/topotests/zebra_nht_resolution/r1/zebra.conf b/tests/topotests/zebra_nht_resolution/r1/zebra.conf
new file mode 100644
index 0000000..6c35c5c
--- /dev/null
+++ b/tests/topotests/zebra_nht_resolution/r1/zebra.conf
@@ -0,0 +1,5 @@
+hostname r1
+!
+interface r1-eth0
+ ip address 192.168.120.1/24
+!
diff --git a/tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py b/tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py
new file mode 100644
index 0000000..9a8f7cc
--- /dev/null
+++ b/tests/topotests/zebra_nht_resolution/test_verify_nh_resolution.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+
+#
+# Copyright (c) 2022 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.
+#
+
+"""
+Test is indended for validating zebra NH resolution logic
+"""
+
+import os
+import sys
+import pytest
+
+from lib.common_config import (
+ start_topology,
+ verify_rib,
+ verify_ip_nht,
+ step,
+ create_static_routes,
+)
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+pytestmark = [pytest.mark.sharpd]
+
+#GLOBAL VARIABLES
+NH1 = "2.2.2.32"
+
+def build_topo(tgen):
+ tgen.add_router("r1")
+
+ switch = tgen.add_switch("sw1")
+ switch.add_link(tgen.gears["r1"])
+
+def setup_module(mod):
+ tgen = Topogen(build_topo, mod.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in tgen.routers().items():
+ 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))
+ )
+ tgen.start_router()
+
+def teardown_module(_mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+def test_verify_zebra_nh_resolution(request):
+ tgen = get_topogen()
+ tc_name = request.node.name
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Starting Zebra NH resolution testcase")
+ r1 = tgen.gears["r1"]
+
+ step("Configure static route")
+ input_dict_1 = {
+ "r1": {
+ "static_routes": [
+ {"network": "2.2.2.0/24", "next_hop": "r1-eth0"}
+ ]
+ }
+ }
+
+ result = create_static_routes(tgen, input_dict_1)
+ assert result is True, "Testcase {} : Failed \n Error: {}".format(
+ tc_name, result
+ )
+
+ step("Verify static routes in RIB of R1")
+ input_dict_2 = {
+ "r1": {
+ "static_routes": [
+ {"network": "2.2.2.0/24"}
+ ]
+ }
+ }
+
+ dut = "r1"
+ result = verify_rib(tgen, "ipv4", dut, input_dict_2)
+ assert result is True, "Testcase {} :Failed \n Error: {}".format(
+ tc_name, result)
+
+ step("Set the connected flag on the NH tracking entry")
+ r1.vtysh_cmd("sharp watch nexthop 2.2.2.32 connected")
+
+ step("Verify that NH 2.2.2.32 gets resolved over static route")
+ input_dict_nh = {
+ "r1": {
+ NH1: {
+ "Address": "2.2.2.0/24",
+ "resolvedVia": "static",
+ "nexthops": {"nexthop1": {"Interfcae": "r1-eth0"}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Nexthop is missing in RIB".format(
+ tc_name, result)
+
+ step("Add a .32/32 route with the NH as itself")
+ r1.vtysh_cmd("sharp install routes 2.2.2.32 nexthop 2.2.2.32 1")
+
+ step("Verify that the installation of .32/32 has no effect on the NHT")
+ input_dict_nh = {
+ "r1": {
+ NH1: {
+ "Address": "2.2.2.0/24",
+ "resolvedVia": "static",
+ "nexthops": {"nexthop1": {"Interface": "r1-eth0"}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Nexthop became unresolved".format(
+ tc_name, result)
+
+ step("Add a .31/32 route with the NH as 2.2.2.32"
+ "to verify the NH Resolution behaviour")
+ r1.vtysh_cmd("sharp install routes 2.2.2.31 nexthop 2.2.2.32 1")
+
+ step("Verify that NH 2.2.2.2/32 doesn't become unresolved")
+ input_dict_nh = {
+ "r1": {
+ NH1: {
+ "Address": "2.2.2.0/24",
+ "resolvedVia": "static",
+ "nexthops": {"nexthop1": {"Interface": "r1-eth0"}},
+ }
+ }
+ }
+ result = verify_ip_nht(tgen, input_dict_nh)
+ assert result is True, "Testcase {} : Failed \n"
+ "Error: Nexthop became unresolved".format(
+ tc_name, result)
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_opaque/__init__.py b/tests/topotests/zebra_opaque/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/zebra_opaque/__init__.py
diff --git a/tests/topotests/zebra_opaque/r1/bgpd.conf b/tests/topotests/zebra_opaque/r1/bgpd.conf
new file mode 100644
index 0000000..3831360
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r1/bgpd.conf
@@ -0,0 +1,7 @@
+!
+bgp send-extra-data zebra
+!
+router bgp 65001
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.2 remote-as external
+!
diff --git a/tests/topotests/zebra_opaque/r1/zebra.conf b/tests/topotests/zebra_opaque/r1/zebra.conf
new file mode 100644
index 0000000..b29940f
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r1/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r1-eth0
+ ip address 192.168.1.1/24
+!
diff --git a/tests/topotests/zebra_opaque/r2/bgpd.conf b/tests/topotests/zebra_opaque/r2/bgpd.conf
new file mode 100644
index 0000000..ec47605
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r2/bgpd.conf
@@ -0,0 +1,15 @@
+!
+bgp send-extra-data zebra
+!
+router bgp 65002
+ no bgp ebgp-requires-policy
+ neighbor 192.168.1.1 remote-as external
+ address-family ipv4 unicast
+ redistribute connected
+ neighbor 192.168.1.1 route-map r1 out
+ exit-address-family
+!
+route-map r1 permit 10
+ set community 65002:1 65002:2
+ set large-community 65002:1:1 65002:2:1
+!
diff --git a/tests/topotests/zebra_opaque/r2/zebra.conf b/tests/topotests/zebra_opaque/r2/zebra.conf
new file mode 100644
index 0000000..cffe827
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r2/zebra.conf
@@ -0,0 +1,4 @@
+!
+int r2-eth0
+ ip address 192.168.1.2/24
+!
diff --git a/tests/topotests/zebra_opaque/r3/ospf6d.conf b/tests/topotests/zebra_opaque/r3/ospf6d.conf
new file mode 100644
index 0000000..7782f40
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r3/ospf6d.conf
@@ -0,0 +1,9 @@
+!
+interface r3-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 send-extra-data zebra
+!
diff --git a/tests/topotests/zebra_opaque/r3/ospfd.conf b/tests/topotests/zebra_opaque/r3/ospfd.conf
new file mode 100644
index 0000000..f149267
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r3/ospfd.conf
@@ -0,0 +1,9 @@
+!
+interface r3-eth0
+ ip ospf area 0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf send-extra-data zebra
+!
diff --git a/tests/topotests/zebra_opaque/r3/zebra.conf b/tests/topotests/zebra_opaque/r3/zebra.conf
new file mode 100644
index 0000000..744f2f1
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r3/zebra.conf
@@ -0,0 +1,5 @@
+!
+int r3-eth0
+ ip address 192.168.1.1/24
+ ipv6 address 2001:db8:1::1/64
+!
diff --git a/tests/topotests/zebra_opaque/r4/ospf6d.conf b/tests/topotests/zebra_opaque/r4/ospf6d.conf
new file mode 100644
index 0000000..5118a53
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r4/ospf6d.conf
@@ -0,0 +1,9 @@
+!
+interface r4-eth0
+ ipv6 ospf6 area 0
+ ipv6 ospf6 hello-interval 2
+ ipv6 ospf6 dead-interval 10
+!
+router ospf6
+ ospf6 send-extra-data zebra
+!
diff --git a/tests/topotests/zebra_opaque/r4/ospfd.conf b/tests/topotests/zebra_opaque/r4/ospfd.conf
new file mode 100644
index 0000000..0da181b
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r4/ospfd.conf
@@ -0,0 +1,9 @@
+!
+interface r4-eth0
+ ip ospf area 0
+ ip ospf hello-interval 2
+ ip ospf dead-interval 10
+!
+router ospf
+ ospf send-extra-data zebra
+!
diff --git a/tests/topotests/zebra_opaque/r4/zebra.conf b/tests/topotests/zebra_opaque/r4/zebra.conf
new file mode 100644
index 0000000..5916f5f
--- /dev/null
+++ b/tests/topotests/zebra_opaque/r4/zebra.conf
@@ -0,0 +1,5 @@
+!
+int r4-eth0
+ ip address 192.168.1.2/24
+ ipv6 address 2001:db8:1::2/64
+!
diff --git a/tests/topotests/zebra_opaque/test_zebra_opaque.py b/tests/topotests/zebra_opaque/test_zebra_opaque.py
new file mode 100644
index 0000000..202e28a
--- /dev/null
+++ b/tests/topotests/zebra_opaque/test_zebra_opaque.py
@@ -0,0 +1,130 @@
+#!/usr/bin/env python
+
+# Copyright (c) 2021 by
+# Donatas Abraitis <donatas.abraitis@gmail.com>
+#
+# 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 if Opaque Data is accessable from other daemons in Zebra
+"""
+
+import os
+import sys
+import json
+import pytest
+import functools
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+
+pytestmark = [pytest.mark.bgpd, pytest.mark.ospfd, pytest.mark.ospf6d]
+
+
+def setup_module(mod):
+ topodef = {"s1": ("r1", "r2"), "s2": ("r3", "r4")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+
+ for i, (rname, router) in enumerate(router_list.items(), 1):
+ router.load_config(
+ TopoRouter.RD_ZEBRA, os.path.join(CWD, "{}/zebra.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_BGP, os.path.join(CWD, "{}/bgpd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF, os.path.join(CWD, "{}/ospfd.conf".format(rname))
+ )
+ router.load_config(
+ TopoRouter.RD_OSPF6, os.path.join(CWD, "{}/ospf6d.conf".format(rname))
+ )
+
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_zebra_opaque():
+ tgen = get_topogen()
+
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+
+ def _bgp_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip route 192.168.1.0/24 json"))
+ expected = {
+ "192.168.1.0/24": [
+ {
+ "communities": "65002:1 65002:2",
+ "largeCommunities": "65002:1:1 65002:2:1",
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _ospf_converge(router):
+ output = json.loads(router.vtysh_cmd("show ip route 192.168.1.0/24 json"))
+ expected = {
+ "192.168.1.0/24": [
+ {
+ "ospfPathType": "Intra-Area",
+ "ospfAreaId": "0.0.0.0",
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ def _ospf6_converge(router):
+ output = json.loads(router.vtysh_cmd("show ipv6 route 2001:db8:1::/64 json"))
+ expected = {
+ "2001:db8:1::/64": [
+ {
+ "ospfPathType": "Intra-Area",
+ "ospfAreaId": "0.0.0.0",
+ }
+ ]
+ }
+ return topotest.json_cmp(output, expected)
+
+ router = tgen.gears["r1"]
+ test_func = functools.partial(_bgp_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, 'Cannot see BGP community aliases "{}"'.format(router)
+
+ router = tgen.gears["r3"]
+ test_func = functools.partial(_ospf_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, 'Cannot see OSPFv2 opaque attributes "{}"'.format(router)
+
+ router = tgen.gears["r3"]
+ test_func = functools.partial(_ospf6_converge, router)
+ success, result = topotest.run_and_expect(test_func, None, count=60, wait=0.5)
+ assert result is None, 'Cannot see OSPFv3 opaque attributes "{}"'.format(router)
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_rib/r1/iproute.ref b/tests/topotests/zebra_rib/r1/iproute.ref
new file mode 100644
index 0000000..b28182c
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/iproute.ref
@@ -0,0 +1,512 @@
+4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric 4278198272
+4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric 16777217
+4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric 167772161
+4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric 2684354561
+10.0.0.0 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.1 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.2 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.3 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.4 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.5 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.6 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.7 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.8 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.9 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.10 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.11 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.12 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.13 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.14 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.15 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.16 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.17 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.18 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.19 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.20 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.21 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.22 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.23 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.24 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.25 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.26 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.27 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.28 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.29 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.30 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.31 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.32 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.33 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.34 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.35 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.36 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.37 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.38 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.39 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.40 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.41 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.42 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.43 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.45 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.46 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.47 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.48 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.49 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.50 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.51 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.52 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.53 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.54 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.55 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.56 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.57 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.58 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.59 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.60 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.61 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.62 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.63 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.64 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.65 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.66 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.67 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.68 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.69 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.70 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.71 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.72 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.73 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.74 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.75 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.76 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.77 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.78 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.79 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.80 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.81 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.82 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.83 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.84 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.85 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.86 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.87 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.88 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.89 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.90 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.91 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.92 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.93 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.94 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.95 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.96 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.97 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.98 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.99 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.101 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.102 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.103 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.104 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.105 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.106 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.107 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.108 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.109 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.110 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.111 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.112 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.113 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.114 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.115 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.116 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.117 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.118 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.119 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.120 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.121 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.122 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.123 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.124 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.125 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.126 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.127 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.128 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.129 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.130 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.131 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.132 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.133 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.134 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.135 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.136 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.137 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.138 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.139 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.140 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.141 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.142 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.143 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.144 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.145 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.146 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.147 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.148 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.149 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.150 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.151 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.152 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.153 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.154 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.155 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.156 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.157 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.158 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.159 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.160 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.161 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.162 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.163 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.164 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.165 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.166 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.167 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.168 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.169 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.170 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.171 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.172 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.173 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.174 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.175 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.176 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.177 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.178 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.179 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.180 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.181 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.182 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.183 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.184 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.185 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.186 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.187 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.188 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.189 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.190 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.191 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.192 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.193 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.194 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.195 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.196 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.197 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.198 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.199 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.200 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.201 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.202 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.203 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.204 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.205 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.206 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.207 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.208 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.209 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.210 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.211 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.212 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.213 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.214 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.215 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.216 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.217 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.218 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.219 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.220 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.221 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.222 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.223 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.224 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.225 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.226 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.227 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.228 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.229 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.230 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.231 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.232 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.233 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.234 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.235 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.236 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.237 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.238 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.239 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.240 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.241 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.242 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.243 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.244 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.245 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.246 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.247 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.248 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.249 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.250 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.251 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.252 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.253 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.254 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.0.255 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.213.1 metric 20
+10.0.1.0 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.1 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.2 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.3 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.4 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.5 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.6 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.7 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.8 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.9 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.10 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.11 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.12 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.13 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.14 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.15 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.16 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.17 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.18 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.19 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.20 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.21 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.22 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.23 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.24 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.25 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.26 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.27 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.28 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.29 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.30 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.31 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.32 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.33 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.34 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.35 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.36 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.37 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.38 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.39 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.40 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.41 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.42 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.43 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.44 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.45 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.46 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.47 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.48 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.49 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.50 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.51 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.52 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.53 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.54 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.55 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.56 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.57 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.58 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.59 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.60 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.61 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.62 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.63 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.64 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.65 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.66 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.67 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.68 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.69 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.70 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.71 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.72 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.73 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.74 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.75 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.76 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.77 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.78 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.79 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.80 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.81 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.82 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.83 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.84 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.85 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.86 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.87 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.88 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.89 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.90 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.91 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.92 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.93 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.94 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.95 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.96 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.97 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.98 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.99 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.101 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.102 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.103 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.104 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.105 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.106 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.107 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.108 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.109 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.110 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.111 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.112 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.113 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.114 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.115 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.116 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.117 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.118 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.119 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.120 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.121 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.122 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.123 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.124 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.125 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.126 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.127 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.128 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.129 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.130 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.131 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.132 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.133 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.134 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.135 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.136 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.137 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.138 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.139 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.140 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.141 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.142 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.143 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.144 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.145 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.146 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.147 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.148 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.149 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.150 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.151 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.152 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.153 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.154 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.155 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.156 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.157 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.158 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.159 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.160 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.161 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.162 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.163 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.164 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.165 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.166 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.167 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.168 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.169 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.170 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.171 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.172 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.173 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.174 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.175 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.176 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.177 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.178 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.179 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.180 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.181 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.182 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.183 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.184 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.185 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.186 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.187 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.188 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.189 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.190 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.191 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.192 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.193 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.194 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.195 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.196 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.197 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.198 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.199 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.200 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.201 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.202 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.203 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.204 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.205 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.206 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.207 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.208 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.209 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.210 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.211 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.212 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.213 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.214 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.215 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.216 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.217 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.218 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.219 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.220 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.221 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.222 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.223 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.224 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.225 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.226 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.227 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.228 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.229 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.230 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.231 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.232 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.233 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.234 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.235 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.236 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.237 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.238 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.239 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.240 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.241 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.242 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.0.1.243 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.214.1 metric 20
+10.100.100.100 via 192.168.216.3 dev r1-eth6 proto XXXX src 192.168.215.1 metric 20
+192.168.210.0/24 dev r1-eth0 proto XXXX scope link src 192.168.210.1
+192.168.211.0/24 dev r1-eth1 proto XXXX scope link src 192.168.211.1
+192.168.212.0/24 dev r1-eth2 proto XXXX scope link src 192.168.212.1
+192.168.213.0/24 dev r1-eth3 proto XXXX scope link src 192.168.213.1
+192.168.214.0/24 dev r1-eth4 proto XXXX scope link src 192.168.214.1
+192.168.215.0/24 dev r1-eth5 proto XXXX scope link src 192.168.215.1
+192.168.216.0/24 dev r1-eth6 proto XXXX scope link src 192.168.216.1
+192.168.217.0/24 dev r1-eth7 proto XXXX scope link src 192.168.217.1
diff --git a/tests/topotests/zebra_rib/r1/sharp_rmap.ref b/tests/topotests/zebra_rib/r1/sharp_rmap.ref
new file mode 100644
index 0000000..47a9eb6
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/sharp_rmap.ref
@@ -0,0 +1,17 @@
+ZEBRA:
+route-map: sharp Invoked: 500 Optimization: enabled Processed Change: false
+ permit, sequence 10 Invoked 244
+ Match clauses:
+ ip address 10
+ Set clauses:
+ src 192.168.214.1
+ Call clause:
+ Action:
+ Exit routemap
+ permit, sequence 20 Invoked 256
+ Match clauses:
+ Set clauses:
+ src 192.168.213.1
+ Call clause:
+ Action:
+ Exit routemap
diff --git a/tests/topotests/zebra_rib/r1/static_rmap.ref b/tests/topotests/zebra_rib/r1/static_rmap.ref
new file mode 100644
index 0000000..2de98bd
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/static_rmap.ref
@@ -0,0 +1,9 @@
+ZEBRA:
+route-map: static Invoked: 2 Optimization: enabled Processed Change: false
+ permit, sequence 10 Invoked 2
+ Match clauses:
+ Set clauses:
+ src 192.168.215.1
+ Call clause:
+ Action:
+ Exit routemap
diff --git a/tests/topotests/zebra_rib/r1/v4_route_1.json b/tests/topotests/zebra_rib/r1/v4_route_1.json
new file mode 100644
index 0000000..a238e52
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/v4_route_1.json
@@ -0,0 +1,24 @@
+{
+ "4.5.1.0\/24":[
+ {
+ "prefix":"4.5.1.0\/24",
+ "protocol":"kernel",
+ "selected":true,
+ "destSelected":true,
+ "distance":255,
+ "metric":8192,
+ "installed":true,
+ "table":254,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.210.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json
new file mode 100644
index 0000000..5bc665b
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/v4_route_1_static_override.json
@@ -0,0 +1,40 @@
+{
+ "4.5.1.0\/24":[
+ {
+ "prefix":"4.5.1.0\/24",
+ "protocol":"static",
+ "selected":true,
+ "destSelected":true,
+ "distance":1,
+ "metric":0,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.216.3",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth6",
+ "active":true
+ }
+ ]
+ },
+ {
+ "prefix":"4.5.1.0\/24",
+ "protocol":"kernel",
+ "distance":255,
+ "metric":8192,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.210.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth0",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_rib/r1/v4_route_2.json b/tests/topotests/zebra_rib/r1/v4_route_2.json
new file mode 100644
index 0000000..5454af8
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/v4_route_2.json
@@ -0,0 +1,23 @@
+{
+ "4.5.2.0\/24":[
+ {
+ "prefix":"4.5.2.0\/24",
+ "protocol":"kernel",
+ "selected":true,
+ "destSelected":true,
+ "distance":1,
+ "metric":1,
+ "installed":true,
+ "nexthops":[
+ {
+ "flags":3,
+ "fib":true,
+ "ip":"192.168.211.2",
+ "afi":"ipv4",
+ "interfaceName":"r1-eth1",
+ "active":true
+ }
+ ]
+ }
+ ]
+}
diff --git a/tests/topotests/zebra_rib/r1/zebra.conf b/tests/topotests/zebra_rib/r1/zebra.conf
new file mode 100644
index 0000000..b914f2d
--- /dev/null
+++ b/tests/topotests/zebra_rib/r1/zebra.conf
@@ -0,0 +1,26 @@
+hostname r1
+!
+interface r1-eth0
+ ip address 192.168.210.1/24
+!
+interface r1-eth1
+ ip address 192.168.211.1/24
+!
+interface r1-eth2
+ ip address 192.168.212.1/24
+!
+interface r1-eth3
+ ip address 192.168.213.1/24
+!
+interface r1-eth4
+ ip address 192.168.214.1/24
+!
+interface r1-eth5
+ ip address 192.168.215.1/24
+!
+interface r1-eth6
+ ip address 192.168.216.1/24
+!
+interface r1-eth7
+ ip address 192.168.217.1/24
+!
diff --git a/tests/topotests/zebra_rib/test_zebra_rib.py b/tests/topotests/zebra_rib/test_zebra_rib.py
new file mode 100644
index 0000000..b72691b
--- /dev/null
+++ b/tests/topotests/zebra_rib/test_zebra_rib.py
@@ -0,0 +1,340 @@
+#!/usr/bin/env python
+#
+# test_zebra_rib.py
+#
+# Copyright (c) 2019 by
+# Cumulus Networks, Inc
+# Donald Sharp
+#
+# 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_rib.py: Test some basic zebra <-> kernel interactions
+"""
+
+import os
+import re
+import sys
+from functools import partial
+import pytest
+import json
+import platform
+
+# 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 time import sleep
+
+
+pytestmark = [pytest.mark.sharpd]
+krel = platform.release()
+
+
+def config_macvlan(tgen, r_str, device, macvlan):
+ "Creates specified macvlan interace on physical device"
+
+ if topotest.version_cmp(krel, "5.1") < 0:
+ return
+
+ router = tgen.gears[r_str]
+ router.run(
+ "ip link add {} link {} type macvlan mode bridge".format(macvlan, device)
+ )
+ router.run("ip link set {} up".format(macvlan))
+
+
+def setup_module(mod):
+ "Sets up the pytest environment"
+ topodef = {"s1": ("r1", "r1", "r1", "r1", "r1", "r1", "r1", "r1")}
+ tgen = Topogen(topodef, mod.__name__)
+ tgen.start_topology()
+
+ router_list = tgen.routers()
+ for rname, router in router_list.items():
+ 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))
+ )
+
+ # Macvlan interface for protodown func test */
+ config_macvlan(tgen, "r1", "r1-eth0", "r1-eth0-macvlan")
+ # Initialize all routers.
+ tgen.start_router()
+
+
+def teardown_module(mod):
+ "Teardown the pytest environment"
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_zebra_kernel_admin_distance():
+ "Test some basic kernel routes added that should be accepted"
+ logger.info("Test some basic kernel routes that should be accepted")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of router(s) failure")
+
+ r1 = tgen.gears["r1"]
+
+ # Route with 255/8192 metric
+
+ distance = 255
+ metric = 8192
+
+ def makekmetric(dist, metric):
+ return (dist << 24) + metric
+
+ r1.run(
+ "ip route add 4.5.1.0/24 via 192.168.210.2 dev r1-eth0 metric "
+ + str(makekmetric(255, 8192))
+ )
+ # Route with 1/1 metric
+ r1.run(
+ "ip route add 4.5.2.0/24 via 192.168.211.2 dev r1-eth1 metric "
+ + str(makekmetric(1, 1))
+ )
+ # Route with 10/1 metric
+ r1.run(
+ "ip route add 4.5.3.0/24 via 192.168.212.2 dev r1-eth2 metric "
+ + str(makekmetric(10, 1))
+ )
+ # Same route with a 160/1 metric
+ r1.run(
+ "ip route add 4.5.3.0/24 via 192.168.213.2 dev r1-eth3 metric "
+ + str(makekmetric(160, 1))
+ )
+
+ # Currently I believe we have a bug here with the same route and different
+ # metric. That needs to be properly resolved. Making a note for
+ # coming back around later and fixing this.
+ # tgen.mininet_cli()
+ for i in range(1, 2):
+ json_file = "{}/r1/v4_route_{}.json".format(CWD, i)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp,
+ r1,
+ "show ip route 4.5.{}.0 json".format(i),
+ expected,
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assertmsg = '"r1" JSON output mismatches'
+ assert result is None, assertmsg
+ # tgen.mininet_cli()
+
+
+def test_zebra_kernel_override():
+ "Test that a FRR route with a lower admin distance takes over"
+ logger.info("Test kernel override with a better admin distance")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("skipped because of previous test failure")
+
+ r1 = tgen.gears["r1"]
+ r1.vtysh_cmd("conf\nip route 4.5.1.0/24 192.168.216.3")
+ json_file = "{}/r1/v4_route_1_static_override.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assert result is None, '"r1" JSON output mismatches'
+
+ logger.info(
+ "Test that the removal of the static route allows the kernel to take back over"
+ )
+ r1.vtysh_cmd("conf\nno ip route 4.5.1.0/24 192.168.216.3")
+ json_file = "{}/r1/v4_route_1.json".format(CWD)
+ expected = json.loads(open(json_file).read())
+
+ test_func = partial(
+ topotest.router_json_cmp, r1, "show ip route 4.5.1.0 json", expected
+ )
+ _, result = topotest.run_and_expect(test_func, None, count=20, wait=0.5)
+ assert result is None, '"r1" JSON output mismatches'
+
+
+def test_route_map_usage():
+ "Test that FRR only reruns over routes associated with the routemap"
+ logger.info("Test that FRR runs on selected re's on route-map changes")
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip("Skipped because of previous test failure")
+
+ thisDir = os.path.dirname(os.path.realpath(__file__))
+
+ r1 = tgen.gears["r1"]
+ # set the delay timer to 1 to improve test coverage (HA)
+ r1.vtysh_cmd("conf\nzebra route-map delay-timer 1")
+ r1.vtysh_cmd("conf\nroute-map static permit 10\nset src 192.168.215.1")
+ r1.vtysh_cmd("conf\naccess-list 5 seq 5 permit 10.0.0.44/32")
+ r1.vtysh_cmd("conf\naccess-list 10 seq 5 permit 10.0.1.0/24")
+ r1.vtysh_cmd(
+ "conf\nroute-map sharp permit 10\nmatch ip address 10\nset src 192.168.214.1"
+ )
+ r1.vtysh_cmd("conf\nroute-map sharp permit 20\nset src 192.168.213.1")
+ r1.vtysh_cmd("conf\nip protocol static route-map static")
+ r1.vtysh_cmd("conf\nip protocol sharp route-map sharp")
+ sleep(4)
+ r1.vtysh_cmd("conf\nip route 10.100.100.100/32 192.168.216.3")
+ r1.vtysh_cmd("conf\nip route 10.100.100.101/32 10.0.0.44")
+ r1.vtysh_cmd("sharp install route 10.0.0.0 nexthop 192.168.216.3 500")
+ sleep(4)
+
+ static_rmapfile = "%s/r1/static_rmap.ref" % (thisDir)
+ expected = open(static_rmapfile).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
+ logger.info(
+ "Does the show route-map static command run the correct number of times"
+ )
+
+ def check_static_map_correct_runs():
+ actual = r1.vtysh_cmd("show route-map static")
+ actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
+ return topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual Route-map output",
+ title2="Expected Route-map output",
+ )
+
+ ok, result = topotest.run_and_expect(
+ check_static_map_correct_runs, "", count=5, wait=1
+ )
+ assert ok, result
+
+ sharp_rmapfile = "%s/r1/sharp_rmap.ref" % (thisDir)
+ expected = open(sharp_rmapfile).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
+ logger.info("Does the show route-map sharp command run the correct number of times")
+
+ def check_sharp_map_correct_runs():
+ actual = r1.vtysh_cmd("show route-map sharp")
+ actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
+ return topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual Route-map output",
+ title2="Expected Route-map output",
+ )
+
+ ok, result = topotest.run_and_expect(
+ check_sharp_map_correct_runs, "", count=5, wait=1
+ )
+ assert ok, result
+
+ logger.info(
+ "Add a extension to the static route-map to see the static route go away"
+ " and test that the routes installed are correct"
+ )
+
+ r1.vtysh_cmd("conf\nroute-map sharp deny 5\nmatch ip address 5")
+ # we are only checking the kernel here as that this will give us the implied
+ # testing of both the route-map and staticd withdrawing the route
+ # let's spot check that the routes were installed correctly
+ # in the kernel
+ sharp_ipfile = "%s/r1/iproute.ref" % (thisDir)
+ expected = open(sharp_ipfile).read().rstrip()
+ expected = ("\n".join(expected.splitlines()) + "\n").rstrip()
+
+ def check_routes_installed():
+ actual = r1.run("ip route show")
+ actual = ("\n".join(actual.splitlines()) + "\n").rstrip()
+ actual = re.sub(r" nhid [0-9][0-9]", "", actual)
+ actual = re.sub(r" proto sharp", " proto XXXX", actual)
+ actual = re.sub(r" proto static", " proto XXXX", actual)
+ actual = re.sub(r" proto 194", " proto XXXX", actual)
+ actual = re.sub(r" proto 196", " proto XXXX", actual)
+ actual = re.sub(r" proto kernel", " proto XXXX", actual)
+ actual = re.sub(r" proto 2", " proto XXXX", actual)
+ # Some platforms have double spaces? Why??????
+ actual = re.sub(r" proto XXXX ", " proto XXXX ", actual)
+ actual = re.sub(r" metric", " metric", actual)
+ actual = re.sub(r" link ", " link ", actual)
+ return topotest.get_textdiff(
+ actual,
+ expected,
+ title1="Actual ip route show",
+ title2="Expected ip route show",
+ )
+
+ ok, result = topotest.run_and_expect(check_routes_installed, "", count=5, wait=1)
+ assert ok, result
+
+
+def test_protodown():
+ "Run protodown basic functionality test and report results."
+ pdown = False
+ count = 0
+ tgen = get_topogen()
+ if topotest.version_cmp(krel, "5.1") < 0:
+ tgen.errors = "kernel 5.1 needed for protodown tests"
+ pytest.skip(tgen.errors)
+
+ r1 = tgen.gears["r1"]
+
+ # Set interface protodown on
+ r1.vtysh_cmd("sharp interface r1-eth0-macvlan protodown")
+
+ # Timeout to wait for dplane to handle it
+ while count < 10:
+ count += 1
+ output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
+ if re.search(r"protodown reasons:.*sharp", output):
+ pdown = True
+ break
+ sleep(1)
+
+ assert pdown is True, "Interface r1-eth0-macvlan not set protodown"
+
+ # Set interface protodown off
+ r1.vtysh_cmd("no sharp interface r1-eth0-macvlan protodown")
+
+ # Timeout to wait for dplane to handle it
+ while count < 10:
+ count += 1
+ output = r1.vtysh_cmd("show interface r1-eth0-macvlan")
+ if not re.search(r"protodown reasons:.*sharp", output):
+ pdown = False
+ break
+ sleep(1)
+
+ assert pdown is False, "Interface r1-eth0-macvlan not set protodown off"
+
+
+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/zebra_seg6_route/r1/routes.json b/tests/topotests/zebra_seg6_route/r1/routes.json
new file mode 100644
index 0000000..50ac4f7
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/r1/routes.json
@@ -0,0 +1,25 @@
+[
+ {
+ "in": {
+ "dest": "1::1",
+ "nh": "2001::2",
+ "sid": "a::"
+ },
+ "out":[{
+ "prefix":"1::1/128",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[{
+ "flags":3,
+ "fib":true,
+ "active":true,
+ "seg6": { "segs": "a::" }
+ }]
+ }]
+ }
+]
diff --git a/tests/topotests/zebra_seg6_route/r1/setup.sh b/tests/topotests/zebra_seg6_route/r1/setup.sh
new file mode 100644
index 0000000..2cb5c4a
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/r1/setup.sh
@@ -0,0 +1,5 @@
+ip link add vrf10 type vrf table 10
+ip link set vrf10 up
+ip link add dum0 type dummy
+ip link set dum0 up
+sysctl -w net.ipv6.conf.dum0.disable_ipv6=0
diff --git a/tests/topotests/zebra_seg6_route/r1/sharpd.conf b/tests/topotests/zebra_seg6_route/r1/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/r1/sharpd.conf
diff --git a/tests/topotests/zebra_seg6_route/r1/zebra.conf b/tests/topotests/zebra_seg6_route/r1/zebra.conf
new file mode 100644
index 0000000..e5e360f
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/r1/zebra.conf
@@ -0,0 +1,13 @@
+log file zebra.log
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+! debug zebra packet
+! debug zebra dplane
+! debug zebra kernel msgdump
+!
+interface dum0
+ ipv6 address 2001::1/64
+!
diff --git a/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
new file mode 100755
index 0000000..399af74
--- /dev/null
+++ b/tests/topotests/zebra_seg6_route/test_zebra_seg6_route.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_seg6_route.py
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+# 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_seg6_route.py: Test seg6 route addition with zapi.
+"""
+
+import os
+import sys
+import pytest
+import json
+from functools import partial
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.sharpd]
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def setup_module(mod):
+ tgen = Topogen({None: "r1"}, mod.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in tgen.routers().items():
+ router.run(
+ "/bin/bash {}".format(os.path.join(CWD, "{}/setup.sh".format(rname)))
+ )
+ 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))
+ )
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_zebra_seg6local_routes():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Test for seg6local route install via ZAPI was start.")
+ r1 = tgen.gears["r1"]
+
+ def check(router, dest, expected):
+ output = json.loads(router.vtysh_cmd("show ipv6 route {} json".format(dest)))
+ output = output.get("{}/128".format(dest))
+ if output is None:
+ return False
+ return topotest.json_cmp(output, expected)
+
+ manifests = open_json_file(os.path.join(CWD, "{}/routes.json".format("r1")))
+ for manifest in manifests:
+ dest = manifest["in"]["dest"]
+ nh = manifest["in"]["nh"]
+ sid = manifest["in"]["sid"]
+
+ r1.vtysh_cmd(
+ "sharp install seg6-routes {} nexthop-seg6 {} encap {} 1".format(
+ dest, nh, sid
+ )
+ )
+ logger.info("CHECK {} {} {}".format(dest, nh, sid))
+ test_func = partial(check, r1, dest, manifest["out"])
+ success, result = topotest.run_and_expect(test_func, None, count=5, wait=1)
+ assert result is None, "Failed"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))
diff --git a/tests/topotests/zebra_seg6local_route/r1/routes.json b/tests/topotests/zebra_seg6local_route/r1/routes.json
new file mode 100644
index 0000000..4cb1c4a
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/routes.json
@@ -0,0 +1,98 @@
+[
+ {
+ "in": {
+ "dest": "1::1",
+ "context": "End"
+ },
+ "out":[{
+ "prefix":"1::1/128",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[{
+ "flags":3,
+ "fib":true,
+ "active":true,
+ "directlyConnected":true,
+ "interfaceName": "dum0",
+ "seg6local": { "action": "End" }
+ }]
+ }]
+ },
+ {
+ "in": {
+ "dest": "2::1",
+ "context": "End_X 2001::1"
+ },
+ "out":[{
+ "prefix":"2::1/128",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[{
+ "flags":3,
+ "fib":true,
+ "active":true,
+ "directlyConnected":true,
+ "interfaceName": "dum0",
+ "seg6local": { "action": "End.X" }
+ }]
+ }]
+ },
+ {
+ "in": {
+ "dest": "3::1",
+ "context": "End_T 10"
+ },
+ "out":[{
+ "prefix":"3::1/128",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[{
+ "flags":3,
+ "fib":true,
+ "active":true,
+ "directlyConnected":true,
+ "interfaceName": "dum0",
+ "seg6local": { "action": "End.T" }
+ }]
+ }]
+ },
+ {
+ "in": {
+ "dest": "4::1",
+ "context": "End_DX4 10.0.0.1"
+ },
+ "out":[{
+ "prefix":"4::1/128",
+ "protocol":"sharp",
+ "selected":true,
+ "destSelected":true,
+ "distance":150,
+ "metric":0,
+ "installed":true,
+ "table":254,
+ "nexthops":[{
+ "flags":3,
+ "fib":true,
+ "active":true,
+ "directlyConnected":true,
+ "interfaceName": "dum0",
+ "seg6local": { "action": "End.DX4" }
+ }]
+ }]
+ }
+]
diff --git a/tests/topotests/zebra_seg6local_route/r1/setup.sh b/tests/topotests/zebra_seg6local_route/r1/setup.sh
new file mode 100644
index 0000000..691adb0
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/setup.sh
@@ -0,0 +1,3 @@
+ip link add dum0 type dummy
+ip link set dum0 up
+sysctl -w net.ipv6.conf.dum0.disable_ipv6=0
diff --git a/tests/topotests/zebra_seg6local_route/r1/sharpd.conf b/tests/topotests/zebra_seg6local_route/r1/sharpd.conf
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/sharpd.conf
diff --git a/tests/topotests/zebra_seg6local_route/r1/zebra.conf b/tests/topotests/zebra_seg6local_route/r1/zebra.conf
new file mode 100644
index 0000000..dee7a91
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/r1/zebra.conf
@@ -0,0 +1,9 @@
+log file zebra.log
+!
+log stdout notifications
+log monitor notifications
+log commands
+!
+! debug zebra packet
+! debug zebra dplane
+! debug zebra kernel msgdump
diff --git a/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py b/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
new file mode 100755
index 0000000..d9cca5c
--- /dev/null
+++ b/tests/topotests/zebra_seg6local_route/test_zebra_seg6local_route.py
@@ -0,0 +1,113 @@
+#!/usr/bin/env python
+
+#
+# test_zebra_seg6local_route.py
+#
+# Copyright (c) 2020 by
+# LINE Corporation, Hiroki Shirokura <slank.dev@gmail.com>
+#
+# 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_seg6local_route.py: Test seg6local route addition with zapi.
+"""
+
+import os
+import sys
+import pytest
+import json
+from functools import partial
+
+CWD = os.path.dirname(os.path.realpath(__file__))
+sys.path.append(os.path.join(CWD, "../"))
+
+# pylint: disable=C0413
+from lib import topotest
+from lib.topogen import Topogen, TopoRouter, get_topogen
+from lib.topolog import logger
+
+pytestmark = [pytest.mark.sharpd]
+
+
+def open_json_file(filename):
+ try:
+ with open(filename, "r") as f:
+ return json.load(f)
+ except IOError:
+ assert False, "Could not read file {}".format(filename)
+
+
+def setup_module(mod):
+ tgen = Topogen({None: "r1"}, mod.__name__)
+ tgen.start_topology()
+ router_list = tgen.routers()
+ for rname, router in tgen.routers().items():
+ router.run(
+ "/bin/bash {}".format(os.path.join(CWD, "{}/setup.sh".format(rname)))
+ )
+ 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))
+ )
+ tgen.start_router()
+
+
+def teardown_module(_mod):
+ tgen = get_topogen()
+ tgen.stop_topology()
+
+
+def test_zebra_seg6local_routes():
+ tgen = get_topogen()
+ if tgen.routers_have_failure():
+ pytest.skip(tgen.errors)
+ logger.info("Test for seg6local route install via ZAPI was start.")
+ r1 = tgen.gears["r1"]
+
+ def check(router, dest, expected):
+ output = json.loads(router.vtysh_cmd("show ipv6 route {} json".format(dest)))
+ output = output.get("{}/128".format(dest))
+ if output is None:
+ return False
+ return topotest.json_cmp(output, expected)
+
+ manifests = open_json_file(os.path.join(CWD, "{}/routes.json".format("r1")))
+ for manifest in manifests:
+ dest = manifest["in"]["dest"]
+ context = manifest["in"]["context"]
+
+ logger.info("CHECK {} {}".format(dest, context))
+
+ r1.vtysh_cmd(
+ "sharp install seg6local-routes {} nexthop-seg6local dum0 {} 1".format(
+ dest, context
+ )
+ )
+ test_func = partial(
+ check,
+ r1,
+ dest,
+ manifest["out"],
+ )
+ success, result = topotest.run_and_expect(test_func, None, count=25, wait=1)
+ assert result is None, "Failed"
+
+
+if __name__ == "__main__":
+ args = ["-s"] + sys.argv[1:]
+ sys.exit(pytest.main(args))